mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-21 04:42:53 +08:00
3078 lines
75 KiB
C
3078 lines
75 KiB
C
/* tc-maxq.c -- assembler code for a MAXQ chip.
|
|
|
|
Copyright 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
|
|
|
|
Contributed by HCL Technologies Pvt. Ltd.
|
|
|
|
Author: Vineet Sharma(vineets@noida.hcltech.com) Inderpreet
|
|
S.(inderpreetb@noida.hcltech.com)
|
|
|
|
This file is part of GAS.
|
|
|
|
GAS is free software; you can redistribute it and/or modify it under the
|
|
terms of the GNU General Public License as published by the Free Software
|
|
Foundation; either version 3, or (at your option) any later version.
|
|
|
|
GAS is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with GAS; see the file COPYING. If not, write to the Free Software
|
|
Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
|
|
|
|
#include "as.h"
|
|
#include "safe-ctype.h"
|
|
#include "subsegs.h"
|
|
#include "dwarf2dbg.h"
|
|
#include "tc-maxq.h"
|
|
#include "opcode/maxq.h"
|
|
#include "ctype.h"
|
|
|
|
#ifndef MAXQ10S
|
|
#define MAXQ10S 1
|
|
#endif
|
|
|
|
#ifndef DEFAULT_ARCH
|
|
#define DEFAULT_ARCH "MAXQ20"
|
|
#endif
|
|
|
|
#ifndef MAX_OPERANDS
|
|
#define MAX_OPERANDS 2
|
|
#endif
|
|
|
|
#ifndef MAX_MNEM_SIZE
|
|
#define MAX_MNEM_SIZE 8
|
|
#endif
|
|
|
|
#ifndef END_OF_INSN
|
|
#define END_OF_INSN '\0'
|
|
#endif
|
|
|
|
#ifndef IMMEDIATE_PREFIX
|
|
#define IMMEDIATE_PREFIX '#'
|
|
#endif
|
|
|
|
#ifndef MAX_REG_NAME_SIZE
|
|
#define MAX_REG_NAME_SIZE 4
|
|
#endif
|
|
|
|
#ifndef MAX_MEM_NAME_SIZE
|
|
#define MAX_MEM_NAME_SIZE 9
|
|
#endif
|
|
|
|
/* opcode for PFX[0]. */
|
|
#define PFX0 0x0b
|
|
|
|
/* Set default to MAXQ20. */
|
|
unsigned int max_version = bfd_mach_maxq20;
|
|
|
|
const char *default_arch = DEFAULT_ARCH;
|
|
|
|
/* Type of the operand: Register,Immediate,Memory access,flag or bit. */
|
|
|
|
union _maxq20_op
|
|
{
|
|
const reg_entry * reg;
|
|
char imms; /* This is to store the immediate value operand. */
|
|
expressionS * disps;
|
|
symbolS * data;
|
|
const mem_access * mem;
|
|
int flag;
|
|
const reg_bit * r_bit;
|
|
};
|
|
|
|
typedef union _maxq20_op maxq20_opcode;
|
|
|
|
/* For handling optional L/S in Maxq20. */
|
|
|
|
/* Exposed For Linker - maps indirectly to the liker relocations. */
|
|
#define LONG_PREFIX MAXQ_LONGJUMP /* BFD_RELOC_16 */
|
|
#define SHORT_PREFIX MAXQ_SHORTJUMP /* BFD_RELOC_16_PCREL_S2 */
|
|
#define ABSOLUTE_ADDR_FOR_DATA MAXQ_INTERSEGMENT
|
|
|
|
#define NO_PREFIX 0
|
|
#define EXPLICT_LONG_PREFIX 14
|
|
|
|
/* The main instruction structure containing fields to describe instrn */
|
|
typedef struct _maxq20_insn
|
|
{
|
|
/* The opcode information for the MAXQ20 */
|
|
MAXQ20_OPCODE_INFO op;
|
|
|
|
/* The number of operands */
|
|
unsigned int operands;
|
|
|
|
/* Number of different types of operands - Comments can be removed if reqd.
|
|
*/
|
|
unsigned int reg_operands, mem_operands, disp_operands, data_operands;
|
|
unsigned int imm_operands, imm_bit_operands, bit_operands, flag_operands;
|
|
|
|
/* Types of the individual operands */
|
|
UNKNOWN_OP types[MAX_OPERANDS];
|
|
|
|
/* Relocation type for operand : to be investigated into */
|
|
int reloc[MAX_OPERANDS];
|
|
|
|
/* Complete information of the Operands */
|
|
maxq20_opcode maxq20_op[MAX_OPERANDS];
|
|
|
|
/* Choice of prefix register whenever needed */
|
|
int prefix;
|
|
|
|
/* Optional Prefix for Instructions like LJUMP, SJUMP etc */
|
|
unsigned char Instr_Prefix;
|
|
|
|
/* 16 bit Instruction word */
|
|
unsigned char instr[2];
|
|
}
|
|
maxq20_insn;
|
|
|
|
/* Definitions of all possible characters that can start an operand. */
|
|
const char *extra_symbol_chars = "@(#";
|
|
|
|
/* Special Character that would start a comment. */
|
|
const char comment_chars[] = ";";
|
|
|
|
/* Starts a comment when it appears at the start of a line. */
|
|
const char line_comment_chars[] = ";#";
|
|
|
|
const char line_separator_chars[] = ""; /* originally may b by sudeep "\n". */
|
|
|
|
/* The following are used for option processing. */
|
|
|
|
/* This is added to the mach independent string passed to getopt. */
|
|
const char *md_shortopts = "q";
|
|
|
|
/* Characters for exponent and floating point. */
|
|
const char EXP_CHARS[] = "eE";
|
|
const char FLT_CHARS[] = "";
|
|
|
|
/* This is for the machine dependent option handling. */
|
|
#define OPTION_EB (OPTION_MD_BASE + 0)
|
|
#define OPTION_EL (OPTION_MD_BASE + 1)
|
|
#define MAXQ_10 (OPTION_MD_BASE + 2)
|
|
#define MAXQ_20 (OPTION_MD_BASE + 3)
|
|
|
|
struct option md_longopts[] =
|
|
{
|
|
{"MAXQ10", no_argument, NULL, MAXQ_10},
|
|
{"MAXQ20", no_argument, NULL, MAXQ_20},
|
|
{NULL, no_argument, NULL, 0}
|
|
};
|
|
size_t md_longopts_size = sizeof (md_longopts);
|
|
|
|
/* md_undefined_symbol We have no need for this function. */
|
|
|
|
symbolS *
|
|
md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
maxq_target (int target)
|
|
{
|
|
max_version = target;
|
|
bfd_set_arch_mach (stdoutput, bfd_arch_maxq, max_version);
|
|
}
|
|
|
|
int
|
|
md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
|
|
{
|
|
/* Any options support will be added onto this switch case. */
|
|
switch (c)
|
|
{
|
|
case MAXQ_10:
|
|
max_version = bfd_mach_maxq10;
|
|
break;
|
|
case MAXQ_20:
|
|
max_version = bfd_mach_maxq20;
|
|
break;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* When a usage message is printed, this function is called and
|
|
it prints a description of the machine specific options. */
|
|
|
|
void
|
|
md_show_usage (FILE * stream)
|
|
{
|
|
/* Over here we will fill the description of the machine specific options. */
|
|
|
|
fprintf (stream, _(" MAXQ-specific assembler options:\n"));
|
|
|
|
fprintf (stream, _("\
|
|
-MAXQ20 generate obj for MAXQ20(default)\n\
|
|
-MAXQ10 generate obj for MAXQ10\n\
|
|
"));
|
|
}
|
|
|
|
unsigned long
|
|
maxq20_mach (void)
|
|
{
|
|
if (!(strcmp (default_arch, "MAXQ20")))
|
|
return 0;
|
|
|
|
as_fatal (_("Unknown architecture"));
|
|
return 1;
|
|
}
|
|
|
|
arelent *
|
|
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
|
|
{
|
|
arelent *rel;
|
|
bfd_reloc_code_real_type code;
|
|
|
|
switch (fixp->fx_r_type)
|
|
{
|
|
case MAXQ_INTERSEGMENT:
|
|
case MAXQ_LONGJUMP:
|
|
case BFD_RELOC_16_PCREL_S2:
|
|
code = fixp->fx_r_type;
|
|
break;
|
|
|
|
case 0:
|
|
default:
|
|
switch (fixp->fx_size)
|
|
{
|
|
default:
|
|
as_bad_where (fixp->fx_file, fixp->fx_line,
|
|
_("can not do %d byte relocation"), fixp->fx_size);
|
|
code = BFD_RELOC_32;
|
|
break;
|
|
|
|
case 1:
|
|
code = BFD_RELOC_8;
|
|
break;
|
|
case 2:
|
|
code = BFD_RELOC_16;
|
|
break;
|
|
case 4:
|
|
code = BFD_RELOC_32;
|
|
break;
|
|
}
|
|
}
|
|
|
|
rel = xmalloc (sizeof (arelent));
|
|
rel->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
|
|
*rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
|
|
|
|
rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
|
|
rel->addend = fixp->fx_addnumber;
|
|
rel->howto = bfd_reloc_type_lookup (stdoutput, code);
|
|
|
|
if (rel->howto == NULL)
|
|
{
|
|
as_bad_where (fixp->fx_file, fixp->fx_line,
|
|
_("cannot represent relocation type %s"),
|
|
bfd_get_reloc_code_name (code));
|
|
|
|
/* Set howto to a garbage value so that we can keep going. */
|
|
rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
|
|
gas_assert (rel->howto != NULL);
|
|
}
|
|
|
|
return rel;
|
|
}
|
|
|
|
/* md_estimate_size_before_relax()
|
|
|
|
Called just before relax() for rs_machine_dependent frags. The MAXQ
|
|
assembler uses these frags to handle 16 bit absolute jumps which require a
|
|
prefix instruction to be inserted. Any symbol that is now undefined will
|
|
not become defined. Return the correct fr_subtype in the frag. Return the
|
|
initial "guess for variable size of frag"(This will be eiter 2 or 0) to
|
|
caller. The guess is actually the growth beyond the fixed part. Whatever
|
|
we do to grow the fixed or variable part contributes to our returned
|
|
value. */
|
|
|
|
int
|
|
md_estimate_size_before_relax (fragS *fragP, segT segment)
|
|
{
|
|
/* Check whether the symbol has been resolved or not.
|
|
Otherwise we will have to generate a fixup. */
|
|
if ((S_GET_SEGMENT (fragP->fr_symbol) != segment)
|
|
|| fragP->fr_subtype == EXPLICT_LONG_PREFIX)
|
|
{
|
|
RELOC_ENUM reloc_type;
|
|
unsigned char *opcode;
|
|
int old_fr_fix;
|
|
|
|
/* Now this symbol has not been defined in this file.
|
|
Hence we will have to create a fixup. */
|
|
int size = 2;
|
|
|
|
/* This is for the prefix instruction. */
|
|
|
|
if (fragP->fr_subtype == EXPLICT_LONG_PREFIX)
|
|
fragP->fr_subtype = LONG_PREFIX;
|
|
|
|
if (S_GET_SEGMENT (fragP->fr_symbol) != segment
|
|
&& ((!(fragP->fr_subtype) == EXPLICT_LONG_PREFIX)))
|
|
fragP->fr_subtype = ABSOLUTE_ADDR_FOR_DATA;
|
|
|
|
reloc_type =
|
|
(fragP->fr_subtype ? fragP->fr_subtype : ABSOLUTE_ADDR_FOR_DATA);
|
|
|
|
fragP->fr_subtype = reloc_type;
|
|
|
|
if (reloc_type == SHORT_PREFIX)
|
|
size = 0;
|
|
old_fr_fix = fragP->fr_fix;
|
|
opcode = (unsigned char *) fragP->fr_opcode;
|
|
|
|
fragP->fr_fix += (size);
|
|
|
|
fix_new (fragP, old_fr_fix - 2, size + 2,
|
|
fragP->fr_symbol, fragP->fr_offset, 0, reloc_type);
|
|
frag_wane (fragP);
|
|
return fragP->fr_fix - old_fr_fix;
|
|
}
|
|
|
|
if (fragP->fr_subtype == SHORT_PREFIX)
|
|
{
|
|
fragP->fr_subtype = SHORT_PREFIX;
|
|
return 0;
|
|
}
|
|
|
|
if (fragP->fr_subtype == NO_PREFIX || fragP->fr_subtype == LONG_PREFIX)
|
|
{
|
|
unsigned long instr;
|
|
unsigned long call_addr;
|
|
long diff;
|
|
fragS *f;
|
|
diff = diff ^ diff;;
|
|
call_addr = call_addr ^ call_addr;
|
|
instr = 0;
|
|
f = NULL;
|
|
|
|
/* segment_info_type *seginfo = seg_info (segment); */
|
|
instr = fragP->fr_address + fragP->fr_fix - 2;
|
|
|
|
/* This is the offset if it is a PC relative jump. */
|
|
call_addr = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset;
|
|
|
|
/* PC stores the value of the next instruction. */
|
|
diff = (call_addr - instr) - 1;
|
|
|
|
if (diff >= (-128 * 2) && diff <= (2 * 127))
|
|
{
|
|
/* Now as offset is an 8 bit value, we will pass
|
|
that to the jump instruction directly. */
|
|
fragP->fr_subtype = NO_PREFIX;
|
|
return 0;
|
|
}
|
|
|
|
fragP->fr_subtype = LONG_PREFIX;
|
|
return 2;
|
|
}
|
|
|
|
as_fatal (_("Illegal Reloc type in md_estimate_size_before_relax for line : %d"),
|
|
frag_now->fr_line);
|
|
return 0;
|
|
}
|
|
|
|
char *
|
|
md_atof (int type, char * litP, int * sizeP)
|
|
{
|
|
if (type == 'd')
|
|
/* The size of Double has been changed to 2 words ie 32 bits. */
|
|
type = 'f';
|
|
return ieee_md_atof (type, litP, sizeP, FALSE);
|
|
}
|
|
|
|
void
|
|
maxq20_cons_fix_new (fragS * frag, unsigned int off, unsigned int len,
|
|
expressionS * exp)
|
|
{
|
|
int r = 0;
|
|
|
|
switch (len)
|
|
{
|
|
case 2:
|
|
r = MAXQ_WORDDATA; /* Word+n */
|
|
break;
|
|
case 4:
|
|
r = MAXQ_LONGDATA; /* Long+n */
|
|
break;
|
|
}
|
|
|
|
fix_new_exp (frag, off, len, exp, 0, r);
|
|
return;
|
|
}
|
|
|
|
/* GAS will call this for every rs_machine_dependent fragment. The
|
|
instruction is completed using the data from the relaxation pass. It may
|
|
also create any necessary relocations. */
|
|
void
|
|
md_convert_frag (bfd * headers ATTRIBUTE_UNUSED,
|
|
segT seg ATTRIBUTE_UNUSED,
|
|
fragS * fragP)
|
|
{
|
|
char *opcode;
|
|
offsetT target_address;
|
|
offsetT opcode_address;
|
|
offsetT displacement_from_opcode_start;
|
|
int address;
|
|
|
|
opcode = fragP->fr_opcode;
|
|
address = 0;
|
|
target_address = opcode_address = displacement_from_opcode_start = 0;
|
|
|
|
target_address =
|
|
(S_GET_VALUE (fragP->fr_symbol) / MAXQ_OCTETS_PER_BYTE) +
|
|
(fragP->fr_offset / MAXQ_OCTETS_PER_BYTE);
|
|
|
|
opcode_address =
|
|
(fragP->fr_address / MAXQ_OCTETS_PER_BYTE) +
|
|
((fragP->fr_fix - 2) / MAXQ_OCTETS_PER_BYTE);
|
|
|
|
/* PC points to the next Instruction. */
|
|
displacement_from_opcode_start = ((target_address - opcode_address) - 1);
|
|
|
|
if ((displacement_from_opcode_start >= -128
|
|
&& displacement_from_opcode_start <= 127)
|
|
&& (fragP->fr_subtype == SHORT_PREFIX
|
|
|| fragP->fr_subtype == NO_PREFIX))
|
|
{
|
|
/* Its a displacement. */
|
|
*opcode = (char) displacement_from_opcode_start;
|
|
}
|
|
else
|
|
{
|
|
/* Its an absolute 16 bit jump. Now we have to
|
|
load the prefix operator with the upper 8 bits. */
|
|
if (fragP->fr_subtype == SHORT_PREFIX)
|
|
{
|
|
as_bad (_("Cant make long jump/call into short jump/call : %d"),
|
|
fragP->fr_line);
|
|
return;
|
|
}
|
|
|
|
/* Check whether the symbol has been resolved or not.
|
|
Otherwise we will have to generate a fixup. */
|
|
|
|
if (fragP->fr_subtype != SHORT_PREFIX)
|
|
{
|
|
RELOC_ENUM reloc_type;
|
|
int old_fr_fix;
|
|
int size = 2;
|
|
|
|
/* Now this is a basolute jump/call.
|
|
Hence we will have to create a fixup. */
|
|
if (fragP->fr_subtype == NO_PREFIX)
|
|
fragP->fr_subtype = LONG_PREFIX;
|
|
|
|
reloc_type =
|
|
(fragP->fr_subtype ? fragP->fr_subtype : LONG_PREFIX);
|
|
|
|
if (reloc_type == 1)
|
|
size = 0;
|
|
old_fr_fix = fragP->fr_fix;
|
|
|
|
fragP->fr_fix += (size);
|
|
|
|
fix_new (fragP, old_fr_fix - 2, size + 2,
|
|
fragP->fr_symbol, fragP->fr_offset, 0, reloc_type);
|
|
frag_wane (fragP);
|
|
}
|
|
}
|
|
}
|
|
|
|
long
|
|
md_pcrel_from (fixS *fixP)
|
|
{
|
|
return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
|
|
}
|
|
|
|
/* Writes the val to the buf, where n is the nuumber of bytes to write. */
|
|
|
|
void
|
|
maxq_number_to_chars (char *buf, valueT val, int n)
|
|
{
|
|
if (target_big_endian)
|
|
number_to_chars_bigendian (buf, val, n);
|
|
else
|
|
number_to_chars_littleendian (buf, val, n);
|
|
}
|
|
|
|
/* GAS will call this for each fixup. It's main objective is to store the
|
|
correct value in the object file. 'fixup_segment' performs the generic
|
|
overflow check on the 'valueT *val' argument after md_apply_fix returns.
|
|
If the overflow check is relevant for the target machine, then
|
|
'md_apply_fix' should modify 'valueT *val', typically to the value stored
|
|
in the object file (not to be done in MAXQ). */
|
|
|
|
void
|
|
md_apply_fix (fixS *fixP, valueT *valT, segT seg ATTRIBUTE_UNUSED)
|
|
{
|
|
char *p = fixP->fx_frag->fr_literal + fixP->fx_where;
|
|
char *frag_to_fix_at =
|
|
fixP->fx_frag->fr_literal + fixP->fx_frag->fr_fix - 2;
|
|
|
|
if (fixP)
|
|
{
|
|
if (fixP->fx_frag && valT)
|
|
{
|
|
/* If the relaxation substate is not defined we make it equal
|
|
to the kind of relocation the fixup is generated for. */
|
|
if (!fixP->fx_frag->fr_subtype)
|
|
fixP->fx_frag->fr_subtype = fixP->fx_r_type;
|
|
|
|
/* For any instruction in which either we have specified an
|
|
absolute address or it is a long jump we need to add a PFX0
|
|
instruction to it. In this case as the instruction has already
|
|
being written at 'fx_where' in the frag we copy it at the end of
|
|
the frag(which is where the relocation was generated) as when
|
|
the relocation is generated the frag is grown by 2 type, this is
|
|
where we copy the contents of fx_where and add a pfx0 at
|
|
fx_where. */
|
|
if ((fixP->fx_frag->fr_subtype == ABSOLUTE_ADDR_FOR_DATA)
|
|
|| (fixP->fx_frag->fr_subtype == LONG_PREFIX))
|
|
{
|
|
*(frag_to_fix_at + 1) = *(p + 1);
|
|
maxq_number_to_chars (p + 1, PFX0, 1);
|
|
}
|
|
|
|
/* Remember value for tc_gen_reloc. */
|
|
fixP->fx_addnumber = *valT;
|
|
}
|
|
|
|
/* Some fixups generated by GAS which gets resovled before this this
|
|
func. is called need to be wriiten to the frag as here we are going
|
|
to go away with the relocations fx_done=1. */
|
|
if (fixP->fx_addsy == NULL)
|
|
{
|
|
maxq_number_to_chars (p, *valT, fixP->fx_size);
|
|
fixP->fx_addnumber = *valT;
|
|
fixP->fx_done = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Tables for lexical analysis. */
|
|
static char mnemonic_chars[256];
|
|
static char register_chars[256];
|
|
static char operand_chars[256];
|
|
static char identifier_chars[256];
|
|
static char digit_chars[256];
|
|
|
|
/* Lexical Macros. */
|
|
#define is_mnemonic_char(x) (mnemonic_chars[(unsigned char)(x)])
|
|
#define is_register_char(x) (register_chars[(unsigned char)(x)])
|
|
#define is_operand_char(x) (operand_chars[(unsigned char)(x)])
|
|
#define is_space_char(x) (x==' ')
|
|
#define is_identifier_char(x) (identifier_chars[(unsigned char)(x)])
|
|
#define is_digit_char(x) (identifier_chars[(unsigned char)(x)])
|
|
|
|
/* Special characters for operands. */
|
|
static char operand_special_chars[] = "[]@.-+";
|
|
|
|
/* md_assemble() will always leave the instruction passed to it unaltered.
|
|
To do this we store the instruction in a special stack. */
|
|
static char save_stack[32];
|
|
static char *save_stack_p;
|
|
|
|
#define END_STRING_AND_SAVE(s) \
|
|
do \
|
|
{ \
|
|
*save_stack_p++ = *(s); \
|
|
*s = '\0'; \
|
|
} \
|
|
while (0)
|
|
|
|
#define RESTORE_END_STRING(s) \
|
|
do \
|
|
{ \
|
|
*(s) = *(--save_stack_p); \
|
|
} \
|
|
while (0)
|
|
|
|
/* The instruction we are assembling. */
|
|
static maxq20_insn i;
|
|
|
|
/* The current template. */
|
|
static MAXQ20_OPCODES *current_templates;
|
|
|
|
/* The displacement operand if any. */
|
|
static expressionS disp_expressions;
|
|
|
|
/* Current Operand we are working on (0:1st operand,1:2nd operand). */
|
|
static int this_operand;
|
|
|
|
/* The prefix instruction if used. */
|
|
static char PFX_INSN[2];
|
|
static char INSERT_BUFFER[2];
|
|
|
|
/* For interface with expression() ????? */
|
|
extern char *input_line_pointer;
|
|
|
|
/* The HASH Tables: */
|
|
|
|
/* Operand Hash Table. */
|
|
static struct hash_control *op_hash;
|
|
|
|
/* Register Hash Table. */
|
|
static struct hash_control *reg_hash;
|
|
|
|
/* Memory reference Hash Table. */
|
|
static struct hash_control *mem_hash;
|
|
|
|
/* Bit hash table. */
|
|
static struct hash_control *bit_hash;
|
|
|
|
/* Memory Access syntax table. */
|
|
static struct hash_control *mem_syntax_hash;
|
|
|
|
/* This is a mapping from pseudo-op names to functions. */
|
|
|
|
const pseudo_typeS md_pseudo_table[] =
|
|
{
|
|
{"int", cons, 2}, /* size of 'int' has been changed to 1 word
|
|
(i.e) 16 bits. */
|
|
{"maxq10", maxq_target, bfd_mach_maxq10},
|
|
{"maxq20", maxq_target, bfd_mach_maxq20},
|
|
{NULL, 0, 0},
|
|
};
|
|
|
|
#define SET_PFX_ARG(x) (PFX_INSN[1] = x)
|
|
|
|
|
|
/* This function sets the PFX value corresponding to the specs. Source
|
|
Destination Index Selection ---------------------------------- Write To|
|
|
SourceRegRange | Dest Addr Range
|
|
------------------------------------------------------ PFX[0] | 0h-Fh |
|
|
0h-7h PFX[1] | 10h-1Fh | 0h-7h PFX[2] | 0h-Fh | 8h-Fh PFX[3] | 10h-1Fh |
|
|
8h-Fh PFX[4] | 0h-Fh | 10h-17h PFX[5] | 10h-1Fh | 10h-17h PFX[6] | 0h-Fh |
|
|
18h-1Fh PFX[7] | 0h-Fh | 18h-1Fh */
|
|
|
|
static void
|
|
set_prefix (void)
|
|
{
|
|
short int src_index = 0, dst_index = 0;
|
|
|
|
if (i.operands == 0)
|
|
return;
|
|
if (i.operands == 1) /* Only SRC is Present */
|
|
{
|
|
if (i.types[0] == REG)
|
|
{
|
|
if (!strcmp (i.op.name, "POP") || !strcmp (i.op.name, "POPI"))
|
|
{
|
|
dst_index = i.maxq20_op[0].reg[0].Mod_index;
|
|
src_index = 0x00;
|
|
}
|
|
else
|
|
{
|
|
src_index = i.maxq20_op[0].reg[0].Mod_index;
|
|
dst_index = 0x00;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i.operands == 2)
|
|
{
|
|
if (i.types[0] == REG && i.types[1] == REG)
|
|
{
|
|
dst_index = i.maxq20_op[0].reg[0].Mod_index;
|
|
src_index = i.maxq20_op[1].reg[0].Mod_index;
|
|
}
|
|
else if (i.types[0] != REG && i.types[1] == REG) /* DST is Absent */
|
|
{
|
|
src_index = i.maxq20_op[1].reg[0].Mod_index;
|
|
dst_index = 0x00;
|
|
}
|
|
else if (i.types[0] == REG && i.types[1] != REG) /* Id SRC is Absent */
|
|
{
|
|
dst_index = i.maxq20_op[0].reg[0].Mod_index;
|
|
src_index = 0x00;
|
|
}
|
|
else if (i.types[0] == BIT && i.maxq20_op[0].r_bit)
|
|
{
|
|
dst_index = i.maxq20_op[0].r_bit->reg->Mod_index;
|
|
src_index = 0x00;
|
|
}
|
|
|
|
else if (i.types[1] == BIT && i.maxq20_op[1].r_bit)
|
|
{
|
|
dst_index = 0x00;
|
|
src_index = i.maxq20_op[1].r_bit->reg->Mod_index;
|
|
}
|
|
}
|
|
|
|
if (src_index >= 0x00 && src_index <= 0xF)
|
|
{
|
|
if (dst_index >= 0x00 && dst_index <= 0x07)
|
|
/* Set PFX[0] */
|
|
i.prefix = 0;
|
|
|
|
else if (dst_index >= 0x08 && dst_index <= 0x0F)
|
|
/* Set PFX[2] */
|
|
i.prefix = 2;
|
|
|
|
else if (dst_index >= 0x10 && dst_index <= 0x17)
|
|
/* Set PFX[4] */
|
|
i.prefix = 4;
|
|
|
|
else if (dst_index >= 0x18 && dst_index <= 0x1F)
|
|
/* Set PFX[6] */
|
|
i.prefix = 6;
|
|
}
|
|
else if (src_index >= 0x10 && src_index <= 0x1F)
|
|
{
|
|
if (dst_index >= 0x00 && dst_index <= 0x07)
|
|
/* Set PFX[1] */
|
|
i.prefix = 1;
|
|
|
|
else if (dst_index >= 0x08 && dst_index <= 0x0F)
|
|
/* Set PFX[3] */
|
|
i.prefix = 3;
|
|
|
|
else if (dst_index >= 0x10 && dst_index <= 0x17)
|
|
/* Set PFX[5] */
|
|
i.prefix = 5;
|
|
|
|
else if (dst_index >= 0x18 && dst_index <= 0x1F)
|
|
/* Set PFX[7] */
|
|
i.prefix = 7;
|
|
}
|
|
}
|
|
|
|
static unsigned char
|
|
is_a_LSinstr (const char *ln_pointer)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; LSInstr[i] != NULL; i++)
|
|
if (!strcmp (LSInstr[i], ln_pointer))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
LS_processing (const char *line)
|
|
{
|
|
if (is_a_LSinstr (line))
|
|
{
|
|
if ((line[0] == 'L') || (line[0] == 'l'))
|
|
{
|
|
i.prefix = 0;
|
|
INSERT_BUFFER[0] = PFX0;
|
|
i.Instr_Prefix = LONG_PREFIX;
|
|
}
|
|
else if ((line[0] == 'S') || (line[0] == 's'))
|
|
i.Instr_Prefix = SHORT_PREFIX;
|
|
else
|
|
i.Instr_Prefix = NO_PREFIX;
|
|
}
|
|
else
|
|
i.Instr_Prefix = LONG_PREFIX;
|
|
}
|
|
|
|
/* Separate mnemonics and the operands. */
|
|
|
|
static char *
|
|
parse_insn (char *line, char *mnemonic)
|
|
{
|
|
char *l = line;
|
|
char *token_start = l;
|
|
char *mnem_p;
|
|
char temp[MAX_MNEM_SIZE];
|
|
int ii = 0;
|
|
|
|
memset (temp, END_OF_INSN, MAX_MNEM_SIZE);
|
|
mnem_p = mnemonic;
|
|
|
|
while ((*mnem_p = mnemonic_chars[(unsigned char) *l]) != 0)
|
|
{
|
|
ii++;
|
|
mnem_p++;
|
|
if (mnem_p >= mnemonic + MAX_MNEM_SIZE)
|
|
{
|
|
as_bad (_("no such instruction: `%s'"), token_start);
|
|
return NULL;
|
|
}
|
|
l++;
|
|
}
|
|
|
|
if (!is_space_char (*l) && *l != END_OF_INSN)
|
|
{
|
|
as_bad (_("invalid character %s in mnemonic"), l);
|
|
return NULL;
|
|
}
|
|
|
|
while (ii)
|
|
{
|
|
temp[ii - 1] = toupper ((char) mnemonic[ii - 1]);
|
|
ii--;
|
|
}
|
|
|
|
LS_processing (temp);
|
|
|
|
if (i.Instr_Prefix != 0 && is_a_LSinstr (temp))
|
|
/* Skip the optional L-S. */
|
|
memcpy (temp, temp + 1, MAX_MNEM_SIZE);
|
|
|
|
/* Look up instruction (or prefix) via hash table. */
|
|
current_templates = (MAXQ20_OPCODES *) hash_find (op_hash, temp);
|
|
|
|
if (current_templates != NULL)
|
|
return l;
|
|
|
|
as_bad (_("no such instruction: `%s'"), token_start);
|
|
return NULL;
|
|
}
|
|
|
|
/* Function to calculate x to the power of y.
|
|
Just to avoid including the math libraries. */
|
|
|
|
static int
|
|
pwr (int x, int y)
|
|
{
|
|
int k, ans = 1;
|
|
|
|
for (k = 0; k < y; k++)
|
|
ans *= x;
|
|
|
|
return ans;
|
|
}
|
|
|
|
static reg_entry *
|
|
parse_reg_by_index (char *imm_start)
|
|
{
|
|
int k = 0, mid = 0, rid = 0, val = 0, j = 0;
|
|
char temp[4] = { 0 };
|
|
reg_entry *reg = NULL;
|
|
|
|
do
|
|
{
|
|
if (isdigit (imm_start[k]))
|
|
temp[k] = imm_start[k] - '0';
|
|
|
|
else if (isalpha (imm_start[k])
|
|
&& (imm_start[k] = tolower (imm_start[k])) < 'g')
|
|
temp[k] = 10 + (int) (imm_start[k] - 'a');
|
|
|
|
else if (imm_start[k] == 'h')
|
|
break;
|
|
|
|
else if (imm_start[k] == END_OF_INSN)
|
|
{
|
|
imm_start[k] = 'd';
|
|
break;
|
|
}
|
|
|
|
else
|
|
return NULL; /* not a hex digit */
|
|
|
|
k++;
|
|
}
|
|
while (imm_start[k] != '\n');
|
|
|
|
switch (imm_start[k])
|
|
{
|
|
case 'h':
|
|
for (j = 0; j < k; j++)
|
|
val += temp[j] * pwr (16, k - j - 1);
|
|
break;
|
|
|
|
case 'd':
|
|
for (j = 0; j < k; j++)
|
|
{
|
|
if (temp[j] > 9)
|
|
return NULL; /* not a number */
|
|
|
|
val += temp[j] * pwr (10, k - j - 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Get the module and register id's. */
|
|
mid = val & 0x0f;
|
|
rid = (val >> 4) & 0x0f;
|
|
|
|
if (mid < 6)
|
|
{
|
|
/* Search the pheripheral reg table. */
|
|
for (j = 0; j < num_of_reg; j++)
|
|
{
|
|
if (new_reg_table[j].opcode == val)
|
|
{
|
|
reg = (reg_entry *) & new_reg_table[j];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
/* Search the system register table. */
|
|
j = 0;
|
|
|
|
while (system_reg_table[j].reg_name != NULL)
|
|
{
|
|
if (system_reg_table[j].opcode == val)
|
|
{
|
|
reg = (reg_entry *) & system_reg_table[j];
|
|
break;
|
|
}
|
|
j++;
|
|
}
|
|
}
|
|
|
|
if (reg == NULL)
|
|
{
|
|
as_bad (_("Invalid register value %s"), imm_start);
|
|
return reg;
|
|
}
|
|
|
|
#if CHANGE_PFX
|
|
if (this_operand == 0 && reg != NULL)
|
|
{
|
|
if (reg->Mod_index > 7)
|
|
i.prefix = 2;
|
|
else
|
|
i.prefix = 0;
|
|
}
|
|
#endif
|
|
return (reg_entry *) reg;
|
|
}
|
|
|
|
/* REG_STRING starts *before* REGISTER_PREFIX. */
|
|
|
|
static reg_entry *
|
|
parse_register (char *reg_string, char **end_op)
|
|
{
|
|
char *s = reg_string;
|
|
char *p = NULL;
|
|
char reg_name_given[MAX_REG_NAME_SIZE + 1];
|
|
reg_entry *r = NULL;
|
|
|
|
r = NULL;
|
|
p = NULL;
|
|
|
|
/* Skip possible REGISTER_PREFIX and possible whitespace. */
|
|
if (is_space_char (*s))
|
|
++s;
|
|
|
|
p = reg_name_given;
|
|
while ((*p++ = register_chars[(unsigned char) *s]) != '\0')
|
|
{
|
|
if (p >= reg_name_given + MAX_REG_NAME_SIZE)
|
|
return (reg_entry *) NULL;
|
|
s++;
|
|
}
|
|
|
|
*end_op = s;
|
|
|
|
r = (reg_entry *) hash_find (reg_hash, reg_name_given);
|
|
|
|
#if CHANGE_PFX
|
|
if (this_operand == 0 && r != NULL)
|
|
{
|
|
if (r->Mod_index > 7)
|
|
i.prefix = 2;
|
|
else
|
|
i.prefix = 0;
|
|
}
|
|
#endif
|
|
return r;
|
|
}
|
|
|
|
static reg_bit *
|
|
parse_register_bit (char *reg_string, char **end_op)
|
|
{
|
|
const char *s = reg_string;
|
|
short k = 0;
|
|
char diff = 0;
|
|
reg_bit *rb = NULL;
|
|
reg_entry *r = NULL;
|
|
bit_name *b = NULL;
|
|
char temp_bitname[MAX_REG_NAME_SIZE + 2];
|
|
char temp[MAX_REG_NAME_SIZE + 1];
|
|
|
|
memset (&temp, '\0', (MAX_REG_NAME_SIZE + 1));
|
|
memset (&temp_bitname, '\0', (MAX_REG_NAME_SIZE + 2));
|
|
|
|
diff = 0;
|
|
r = NULL;
|
|
rb = NULL;
|
|
rb = xmalloc (sizeof (reg_bit));
|
|
rb->reg = xmalloc (sizeof (reg_entry));
|
|
k = 0;
|
|
|
|
/* For supporting bit names. */
|
|
b = (bit_name *) hash_find (bit_hash, reg_string);
|
|
|
|
if (b != NULL)
|
|
{
|
|
*end_op = reg_string + strlen (reg_string);
|
|
strcpy (temp_bitname, b->reg_bit);
|
|
s = temp_bitname;
|
|
}
|
|
|
|
if (strchr (s, '.'))
|
|
{
|
|
while (*s != '.')
|
|
{
|
|
if (*s == '\0')
|
|
return NULL;
|
|
temp[k] = *s++;
|
|
|
|
k++;
|
|
}
|
|
temp[k] = '\0';
|
|
}
|
|
|
|
if ((r = parse_register (temp, end_op)) == NULL)
|
|
return NULL;
|
|
|
|
rb->reg = r;
|
|
|
|
/* Skip the "." */
|
|
s++;
|
|
|
|
if (isdigit ((char) *s))
|
|
rb->bit = atoi (s);
|
|
else if (isalpha ((char) *s))
|
|
{
|
|
rb->bit = (char) *s - 'a';
|
|
rb->bit += 10;
|
|
if (rb->bit > 15)
|
|
{
|
|
as_bad (_("Invalid bit number : '%c'"), (char) *s);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (b != NULL)
|
|
diff = strlen (temp_bitname) - strlen (temp) - 1;
|
|
else
|
|
diff = strlen (reg_string) - strlen (temp) - 1;
|
|
|
|
if (*(s + diff) != '\0')
|
|
{
|
|
as_bad (_("Illegal character after operand '%s'"), reg_string);
|
|
return NULL;
|
|
}
|
|
|
|
return rb;
|
|
}
|
|
|
|
static void
|
|
pfx_for_imm_val (int arg)
|
|
{
|
|
if (i.prefix == -1)
|
|
return;
|
|
|
|
if (i.prefix == 0 && arg == 0 && PFX_INSN[1] == 0 && !(i.data_operands))
|
|
return;
|
|
|
|
if (!(i.prefix < 0) && !(i.prefix > 7))
|
|
PFX_INSN[0] = (i.prefix << 4) | PFX0;
|
|
|
|
if (!PFX_INSN[1])
|
|
PFX_INSN[1] = arg;
|
|
|
|
}
|
|
|
|
static int
|
|
maxq20_immediate (char *imm_start)
|
|
{
|
|
int val = 0, val_pfx = 0;
|
|
char sign_val = 0;
|
|
int k = 0, j;
|
|
int temp[4] = { 0 };
|
|
|
|
imm_start++;
|
|
|
|
if (imm_start[1] == '\0' && (imm_start[0] == '0' || imm_start[0] == '1')
|
|
&& (this_operand == 1 && ((i.types[0] == BIT || i.types[0] == FLAG))))
|
|
{
|
|
val = imm_start[0] - '0';
|
|
i.imm_bit_operands++;
|
|
i.types[this_operand] = IMMBIT;
|
|
i.maxq20_op[this_operand].imms = (char) val;
|
|
#if CHANGE_PFX
|
|
if (i.prefix == 2)
|
|
pfx_for_imm_val (0);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
/* Check For Sign Character. */
|
|
sign_val = 0;
|
|
|
|
do
|
|
{
|
|
if (imm_start[k] == '-' && k == 0)
|
|
sign_val = -1;
|
|
|
|
else if (imm_start[k] == '+' && k == 0)
|
|
sign_val = 1;
|
|
|
|
else if (isdigit (imm_start[k]))
|
|
temp[k] = imm_start[k] - '0';
|
|
|
|
else if (isalpha (imm_start[k])
|
|
&& (imm_start[k] = tolower (imm_start[k])) < 'g')
|
|
temp[k] = 10 + (int) (imm_start[k] - 'a');
|
|
|
|
else if (imm_start[k] == 'h')
|
|
break;
|
|
|
|
else if (imm_start[k] == '\0')
|
|
{
|
|
imm_start[k] = 'd';
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
as_bad (_("Invalid Character in immediate Value : %c"),
|
|
imm_start[k]);
|
|
return 0;
|
|
}
|
|
k++;
|
|
}
|
|
while (imm_start[k] != '\n');
|
|
|
|
switch (imm_start[k])
|
|
{
|
|
case 'h':
|
|
for (j = (sign_val ? 1 : 0); j < k; j++)
|
|
val += temp[j] * pwr (16, k - j - 1);
|
|
break;
|
|
|
|
case 'd':
|
|
for (j = (sign_val ? 1 : 0); j < k; j++)
|
|
{
|
|
if (temp[j] > 9)
|
|
{
|
|
as_bad (_("Invalid Character in immediate value : %c"),
|
|
imm_start[j]);
|
|
return 0;
|
|
}
|
|
val += temp[j] * pwr (10, k - j - 1);
|
|
}
|
|
}
|
|
|
|
if (!sign_val)
|
|
sign_val = 1;
|
|
|
|
/* Now over here the value val stores the 8 bit/16 bit value. We will put a
|
|
check if we are moving a 16 bit immediate value into an 8 bit register.
|
|
In that case we will generate a warning and move only the lower 8 bits */
|
|
if (val > 65535)
|
|
{
|
|
as_bad (_("Immediate value greater than 16 bits"));
|
|
return 0;
|
|
}
|
|
|
|
val = val * sign_val;
|
|
|
|
/* If it is a stack pointer and the value is greater than the maximum
|
|
permissible size */
|
|
if (this_operand == 1)
|
|
{
|
|
if ((val * sign_val) > MAX_STACK && i.types[0] == REG
|
|
&& !strcmp (i.maxq20_op[0].reg->reg_name, "SP"))
|
|
{
|
|
as_warn (_
|
|
("Attempt to move a value in the stack pointer greater than the size of the stack"));
|
|
val = val & MAX_STACK;
|
|
}
|
|
|
|
/* Check the range for 8 bit registers. */
|
|
else if (((val * sign_val) > 0xFF) && (i.types[0] == REG)
|
|
&& (i.maxq20_op[0].reg->rtype == Reg_8W))
|
|
{
|
|
as_warn (_
|
|
("Attempt to move 16 bit value into an 8 bit register.Truncating..\n"));
|
|
val = val & 0xfe;
|
|
}
|
|
|
|
else if (((sign_val == -1) || (val > 0xFF)) && (i.types[0] == REG)
|
|
&& (i.maxq20_op[0].reg->rtype == Reg_8W))
|
|
{
|
|
val_pfx = val >> 8;
|
|
val = ((val) & 0x00ff);
|
|
SET_PFX_ARG (val_pfx);
|
|
i.maxq20_op[this_operand].imms = (char) val;
|
|
}
|
|
|
|
else if ((val <= 0xff) && (i.types[0] == REG)
|
|
&& (i.maxq20_op[0].reg->rtype == Reg_8W))
|
|
i.maxq20_op[this_operand].imms = (char) val;
|
|
|
|
|
|
/* Check for 16 bit registers. */
|
|
else if (((sign_val == -1) || val > 0xFE) && i.types[0] == REG
|
|
&& i.maxq20_op[0].reg->rtype == Reg_16W)
|
|
{
|
|
/* Add PFX for any negative value -> 16bit register. */
|
|
val_pfx = val >> 8;
|
|
val = ((val) & 0x00ff);
|
|
SET_PFX_ARG (val_pfx);
|
|
i.maxq20_op[this_operand].imms = (char) val;
|
|
}
|
|
|
|
else if (val < 0xFF && i.types[0] == REG
|
|
&& i.maxq20_op[0].reg->rtype == Reg_16W)
|
|
{
|
|
i.maxq20_op[this_operand].imms = (char) val;
|
|
}
|
|
|
|
/* All the immediate memory access - no PFX. */
|
|
else if (i.types[0] == MEM)
|
|
{
|
|
if ((sign_val == -1) || val > 0xFE)
|
|
{
|
|
val_pfx = val >> 8;
|
|
val = ((val) & 0x00ff);
|
|
SET_PFX_ARG (val_pfx);
|
|
i.maxq20_op[this_operand].imms = (char) val;
|
|
}
|
|
else
|
|
i.maxq20_op[this_operand].imms = (char) val;
|
|
}
|
|
|
|
/* Special handling for immediate jumps like jump nz, #03h etc. */
|
|
else if (val < 0xFF && i.types[0] == FLAG)
|
|
i.maxq20_op[this_operand].imms = (char) val;
|
|
|
|
else if ((((sign_val == -1) || val > 0xFE)) && i.types[0] == FLAG)
|
|
{
|
|
val_pfx = val >> 8;
|
|
val = ((val) & 0x00ff);
|
|
SET_PFX_ARG (val_pfx);
|
|
i.maxq20_op[this_operand].imms = (char) val;
|
|
}
|
|
else
|
|
{
|
|
as_bad (_("Invalid immediate move operation"));
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* All the instruction with operation on ACC: like ADD src, etc. */
|
|
if ((sign_val == -1) || val > 0xFE)
|
|
{
|
|
val_pfx = val >> 8;
|
|
val = ((val) & 0x00ff);
|
|
SET_PFX_ARG (val_pfx);
|
|
i.maxq20_op[this_operand].imms = (char) val;
|
|
}
|
|
else
|
|
i.maxq20_op[this_operand].imms = (char) val;
|
|
}
|
|
|
|
i.imm_operands++;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
extract_int_val (const char *imm_start)
|
|
{
|
|
int k, j, val;
|
|
char sign_val;
|
|
int temp[4];
|
|
|
|
k = 0;
|
|
j = 0;
|
|
val = 0;
|
|
sign_val = 0;
|
|
do
|
|
{
|
|
if (imm_start[k] == '-' && k == 0)
|
|
sign_val = -1;
|
|
|
|
else if (imm_start[k] == '+' && k == 0)
|
|
sign_val = 1;
|
|
|
|
else if (isdigit (imm_start[k]))
|
|
temp[k] = imm_start[k] - '0';
|
|
|
|
else if (isalpha (imm_start[k]) && (tolower (imm_start[k])) < 'g')
|
|
temp[k] = 10 + (int) (tolower (imm_start[k]) - 'a');
|
|
|
|
else if (tolower (imm_start[k]) == 'h')
|
|
break;
|
|
|
|
else if ((imm_start[k] == '\0') || (imm_start[k] == ']'))
|
|
/* imm_start[k]='d'; */
|
|
break;
|
|
|
|
else
|
|
{
|
|
as_bad (_("Invalid Character in immediate Value : %c"),
|
|
imm_start[k]);
|
|
return 0;
|
|
}
|
|
k++;
|
|
}
|
|
while (imm_start[k] != '\n');
|
|
|
|
switch (imm_start[k])
|
|
{
|
|
case 'h':
|
|
for (j = (sign_val ? 1 : 0); j < k; j++)
|
|
val += temp[j] * pwr (16, k - j - 1);
|
|
break;
|
|
|
|
default:
|
|
for (j = (sign_val ? 1 : 0); j < k; j++)
|
|
{
|
|
if (temp[j] > 9)
|
|
{
|
|
as_bad (_("Invalid Character in immediate value : %c"),
|
|
imm_start[j]);
|
|
return 0;
|
|
}
|
|
val += temp[j] * pwr (10, k - j - 1);
|
|
}
|
|
}
|
|
|
|
if (!sign_val)
|
|
sign_val = 1;
|
|
|
|
return val * sign_val;
|
|
}
|
|
|
|
static char
|
|
check_for_parse (const char *line)
|
|
{
|
|
int val;
|
|
|
|
if (*(line + 1) == '[')
|
|
{
|
|
do
|
|
{
|
|
line++;
|
|
if ((*line == '-') || (*line == '+'))
|
|
break;
|
|
}
|
|
while (!is_space_char (*line));
|
|
|
|
if ((*line == '-') || (*line == '+'))
|
|
val = extract_int_val (line);
|
|
else
|
|
val = extract_int_val (line + 1);
|
|
|
|
INSERT_BUFFER[0] = 0x3E;
|
|
INSERT_BUFFER[1] = val;
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static mem_access *
|
|
maxq20_mem_access (char *mem_string, char **end_op)
|
|
{
|
|
char *s = mem_string;
|
|
char *p;
|
|
char mem_name_given[MAX_MEM_NAME_SIZE + 1];
|
|
mem_access *m;
|
|
|
|
m = NULL;
|
|
|
|
/* Skip possible whitespace. */
|
|
if (is_space_char (*s))
|
|
++s;
|
|
|
|
p = mem_name_given;
|
|
while ((*p++ = register_chars[(unsigned char) *s]) != '\0')
|
|
{
|
|
if (p >= mem_name_given + MAX_MEM_NAME_SIZE)
|
|
return (mem_access *) NULL;
|
|
s++;
|
|
}
|
|
|
|
*end_op = s;
|
|
|
|
m = (mem_access *) hash_find (mem_hash, mem_name_given);
|
|
|
|
return m;
|
|
}
|
|
|
|
/* This function checks whether the operand is a variable in the data segment
|
|
and if so, it returns its symbol entry from the symbol table. */
|
|
|
|
static symbolS *
|
|
maxq20_data (char *op_string)
|
|
{
|
|
symbolS *symbolP;
|
|
symbolP = symbol_find (op_string);
|
|
|
|
if (symbolP != NULL
|
|
&& S_GET_SEGMENT (symbolP) != now_seg
|
|
&& S_GET_SEGMENT (symbolP) != bfd_und_section_ptr)
|
|
{
|
|
/* In case we do not want to always include the prefix instruction and
|
|
let the loader handle the job or in case of a 8 bit addressing mode,
|
|
we will just check for val_pfx to be equal to zero and then load the
|
|
prefix instruction. Otherwise no prefix instruction needs to be
|
|
loaded. */
|
|
/* The prefix register will have to be loaded automatically as we have
|
|
a 16 bit addressing field. */
|
|
pfx_for_imm_val (0);
|
|
return symbolP;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
maxq20_displacement (char *disp_start, char *disp_end)
|
|
{
|
|
expressionS *exp;
|
|
segT exp_seg = 0;
|
|
char *save_input_line_pointer;
|
|
#ifndef LEX_AT
|
|
char *gotfree_input_line;
|
|
#endif
|
|
|
|
gotfree_input_line = NULL;
|
|
exp = &disp_expressions;
|
|
i.maxq20_op[this_operand].disps = exp;
|
|
i.disp_operands++;
|
|
save_input_line_pointer = input_line_pointer;
|
|
input_line_pointer = disp_start;
|
|
|
|
END_STRING_AND_SAVE (disp_end);
|
|
|
|
#ifndef LEX_AT
|
|
/* gotfree_input_line = lex_got (&i.reloc[this_operand], NULL); if
|
|
(gotfree_input_line) input_line_pointer = gotfree_input_line; */
|
|
#endif
|
|
exp_seg = expression (exp);
|
|
|
|
SKIP_WHITESPACE ();
|
|
if (*input_line_pointer)
|
|
as_bad (_("junk `%s' after expression"), input_line_pointer);
|
|
#if GCC_ASM_O_HACK
|
|
RESTORE_END_STRING (disp_end + 1);
|
|
#endif
|
|
RESTORE_END_STRING (disp_end);
|
|
input_line_pointer = save_input_line_pointer;
|
|
#ifndef LEX_AT
|
|
if (gotfree_input_line)
|
|
free (gotfree_input_line);
|
|
#endif
|
|
if (exp->X_op == O_absent || exp->X_op == O_big)
|
|
{
|
|
/* Missing or bad expr becomes absolute 0. */
|
|
as_bad (_("missing or invalid displacement expression `%s' taken as 0"),
|
|
disp_start);
|
|
exp->X_op = O_constant;
|
|
exp->X_add_number = 0;
|
|
exp->X_add_symbol = (symbolS *) 0;
|
|
exp->X_op_symbol = (symbolS *) 0;
|
|
}
|
|
#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT))
|
|
|
|
if (exp->X_op != O_constant
|
|
&& OUTPUT_FLAVOR == bfd_target_aout_flavour
|
|
&& exp_seg != absolute_section
|
|
&& exp_seg != text_section
|
|
&& exp_seg != data_section
|
|
&& exp_seg != bss_section && exp_seg != undefined_section
|
|
&& !bfd_is_com_section (exp_seg))
|
|
{
|
|
as_bad (_("unimplemented segment %s in operand"), exp_seg->name);
|
|
return 0;
|
|
}
|
|
#endif
|
|
i.maxq20_op[this_operand].disps = exp;
|
|
return 1;
|
|
}
|
|
|
|
/* Parse OPERAND_STRING into the maxq20_insn structure I.
|
|
Returns non-zero on error. */
|
|
|
|
static int
|
|
maxq20_operand (char *operand_string)
|
|
{
|
|
reg_entry *r = NULL;
|
|
reg_bit *rb = NULL;
|
|
mem_access *m = NULL;
|
|
char *end_op = NULL;
|
|
symbolS *sym = NULL;
|
|
char *base_string = NULL;
|
|
int ii = 0;
|
|
/* Start and end of displacement string expression (if found). */
|
|
char *displacement_string_start = NULL;
|
|
char *displacement_string_end = NULL;
|
|
/* This maintains the case sentivness. */
|
|
char case_str_op_string[MAX_OPERAND_SIZE + 1];
|
|
char str_op_string[MAX_OPERAND_SIZE + 1];
|
|
char *org_case_op_string = case_str_op_string;
|
|
char *op_string = str_op_string;
|
|
|
|
|
|
memset (op_string, END_OF_INSN, (MAX_OPERAND_SIZE + 1));
|
|
memset (org_case_op_string, END_OF_INSN, (MAX_OPERAND_SIZE + 1));
|
|
|
|
memcpy (op_string, operand_string, strlen (operand_string) + 1);
|
|
memcpy (org_case_op_string, operand_string, strlen (operand_string) + 1);
|
|
|
|
ii = strlen (operand_string) + 1;
|
|
|
|
if (ii > MAX_OPERAND_SIZE)
|
|
{
|
|
as_bad (_("Size of Operand '%s' greater than %d"), op_string,
|
|
MAX_OPERAND_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
while (ii)
|
|
{
|
|
op_string[ii - 1] = toupper ((char) op_string[ii - 1]);
|
|
ii--;
|
|
}
|
|
|
|
if (is_space_char (*op_string))
|
|
++op_string;
|
|
|
|
if (isxdigit (operand_string[0]))
|
|
{
|
|
/* Now the operands can start with an Integer. */
|
|
r = parse_reg_by_index (op_string);
|
|
if (r != NULL)
|
|
{
|
|
if (is_space_char (*op_string))
|
|
++op_string;
|
|
i.types[this_operand] = REG; /* Set the type. */
|
|
i.maxq20_op[this_operand].reg = r; /* Set the Register value. */
|
|
i.reg_operands++;
|
|
return 1;
|
|
}
|
|
|
|
/* Get the original string. */
|
|
memcpy (op_string, operand_string, strlen (operand_string) + 1);
|
|
ii = strlen (operand_string) + 1;
|
|
|
|
while (ii)
|
|
{
|
|
op_string[ii - 1] = toupper ((char) op_string[ii - 1]);
|
|
ii--;
|
|
}
|
|
}
|
|
|
|
/* Check for flags. */
|
|
if (!strcmp (op_string, "Z"))
|
|
{
|
|
if (is_space_char (*op_string))
|
|
++op_string;
|
|
|
|
i.types[this_operand] = FLAG; /* Set the type. */
|
|
i.maxq20_op[this_operand].flag = FLAG_Z; /* Set the Register value. */
|
|
|
|
i.flag_operands++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
else if (!strcmp (op_string, "NZ"))
|
|
{
|
|
if (is_space_char (*op_string))
|
|
++op_string;
|
|
|
|
i.types[this_operand] = FLAG; /* Set the type. */
|
|
i.maxq20_op[this_operand].flag = FLAG_NZ; /* Set the Register value. */
|
|
i.flag_operands++;
|
|
return 1;
|
|
}
|
|
|
|
else if (!strcmp (op_string, "NC"))
|
|
{
|
|
if (is_space_char (*op_string))
|
|
++op_string;
|
|
|
|
i.types[this_operand] = FLAG; /* Set the type. */
|
|
i.maxq20_op[this_operand].flag = FLAG_NC; /* Set the Register value. */
|
|
i.flag_operands++;
|
|
return 1;
|
|
}
|
|
|
|
else if (!strcmp (op_string, "E"))
|
|
{
|
|
if (is_space_char (*op_string))
|
|
++op_string;
|
|
|
|
i.types[this_operand] = FLAG; /* Set the type. */
|
|
i.maxq20_op[this_operand].flag = FLAG_E; /* Set the Register value. */
|
|
|
|
i.flag_operands++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
else if (!strcmp (op_string, "S"))
|
|
{
|
|
if (is_space_char (*op_string))
|
|
++op_string;
|
|
|
|
i.types[this_operand] = FLAG; /* Set the type. */
|
|
i.maxq20_op[this_operand].flag = FLAG_S; /* Set the Register value. */
|
|
|
|
i.flag_operands++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
else if (!strcmp (op_string, "C"))
|
|
{
|
|
if (is_space_char (*op_string))
|
|
++op_string;
|
|
|
|
i.types[this_operand] = FLAG; /* Set the type. */
|
|
i.maxq20_op[this_operand].flag = FLAG_C; /* Set the Register value. */
|
|
|
|
i.flag_operands++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
else if (!strcmp (op_string, "NE"))
|
|
{
|
|
|
|
if (is_space_char (*op_string))
|
|
++op_string;
|
|
|
|
i.types[this_operand] = FLAG; /* Set the type. */
|
|
|
|
i.maxq20_op[this_operand].flag = FLAG_NE; /* Set the Register value. */
|
|
|
|
i.flag_operands++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* CHECK FOR REGISTER BIT */
|
|
else if ((rb = parse_register_bit (op_string, &end_op)) != NULL)
|
|
{
|
|
op_string = end_op;
|
|
|
|
if (is_space_char (*op_string))
|
|
++op_string;
|
|
|
|
i.types[this_operand] = BIT;
|
|
|
|
i.maxq20_op[this_operand].r_bit = rb;
|
|
|
|
i.bit_operands++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
else if (*op_string == IMMEDIATE_PREFIX) /* FOR IMMEDITE. */
|
|
{
|
|
if (is_space_char (*op_string))
|
|
++op_string;
|
|
|
|
i.types[this_operand] = IMM;
|
|
|
|
if (!maxq20_immediate (op_string))
|
|
{
|
|
as_bad (_("illegal immediate operand '%s'"), op_string);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
else if (*op_string == ABSOLUTE_PREFIX || !strcmp (op_string, "NUL"))
|
|
{
|
|
if (is_space_char (*op_string))
|
|
++op_string;
|
|
|
|
/* For new requiremnt of copiler of for, @(BP,cons). */
|
|
if (check_for_parse (op_string))
|
|
{
|
|
memset (op_string, '\0', strlen (op_string) + 1);
|
|
memcpy (op_string, "@BP[OFFS]\0", 11);
|
|
}
|
|
|
|
i.types[this_operand] = MEM;
|
|
|
|
if ((m = maxq20_mem_access (op_string, &end_op)) == NULL)
|
|
{
|
|
as_bad (_("Invalid operand for memory access '%s'"), op_string);
|
|
return 0;
|
|
}
|
|
i.maxq20_op[this_operand].mem = m;
|
|
|
|
i.mem_operands++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
else if ((r = parse_register (op_string, &end_op)) != NULL) /* Check for register. */
|
|
{
|
|
op_string = end_op;
|
|
|
|
if (is_space_char (*op_string))
|
|
++op_string;
|
|
|
|
i.types[this_operand] = REG; /* Set the type. */
|
|
i.maxq20_op[this_operand].reg = r; /* Set the Register value. */
|
|
i.reg_operands++;
|
|
return 1;
|
|
}
|
|
|
|
if (this_operand == 1)
|
|
{
|
|
/* Changed for orginal case of data refrence on 30 Nov 2003. */
|
|
/* The operand can either be a data reference or a symbol reference. */
|
|
if ((sym = maxq20_data (org_case_op_string)) != NULL) /* Check for data memory. */
|
|
{
|
|
while (is_space_char (*op_string))
|
|
++op_string;
|
|
|
|
/* Set the type of the operand. */
|
|
i.types[this_operand] = DATA;
|
|
|
|
/* Set the value of the data. */
|
|
i.maxq20_op[this_operand].data = sym;
|
|
i.data_operands++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
else if (is_digit_char (*op_string) || is_identifier_char (*op_string))
|
|
{
|
|
/* This is a memory reference of some sort. char *base_string;
|
|
Start and end of displacement string expression (if found). char
|
|
*displacement_string_start; char *displacement_string_end. */
|
|
base_string = org_case_op_string + strlen (org_case_op_string);
|
|
|
|
--base_string;
|
|
if (is_space_char (*base_string))
|
|
--base_string;
|
|
|
|
/* If we only have a displacement, set-up for it to be parsed
|
|
later. */
|
|
displacement_string_start = org_case_op_string;
|
|
displacement_string_end = base_string + 1;
|
|
if (displacement_string_start != displacement_string_end)
|
|
{
|
|
if (!maxq20_displacement (displacement_string_start,
|
|
displacement_string_end))
|
|
{
|
|
as_bad (_("illegal displacement operand "));
|
|
return 0;
|
|
}
|
|
/* A displacement operand found. */
|
|
i.types[this_operand] = DISP; /* Set the type. */
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check for displacement. */
|
|
else if (is_digit_char (*op_string) || is_identifier_char (*op_string))
|
|
{
|
|
/* This is a memory reference of some sort. char *base_string;
|
|
Start and end of displacement string expression (if found). char
|
|
*displacement_string_start; char *displacement_string_end; */
|
|
base_string = org_case_op_string + strlen (org_case_op_string);
|
|
|
|
--base_string;
|
|
if (is_space_char (*base_string))
|
|
--base_string;
|
|
|
|
/* If we only have a displacement, set-up for it to be parsed later. */
|
|
displacement_string_start = org_case_op_string;
|
|
displacement_string_end = base_string + 1;
|
|
if (displacement_string_start != displacement_string_end)
|
|
{
|
|
if (!maxq20_displacement (displacement_string_start,
|
|
displacement_string_end))
|
|
return 0;
|
|
/* A displacement operand found. */
|
|
i.types[this_operand] = DISP; /* Set the type. */
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* Parse_operand takes as input instruction and operands and Parse operands
|
|
and makes entry in the template. */
|
|
|
|
static char *
|
|
parse_operands (char *l, const char *mnemonic)
|
|
{
|
|
char *token_start;
|
|
|
|
/* 1 if operand is pending after ','. */
|
|
short int expecting_operand = 0;
|
|
|
|
/* Non-zero if operand parens not balanced. */
|
|
short int paren_not_balanced;
|
|
|
|
int operand_ok;
|
|
|
|
/* For Overcoming Warning of unused variable. */
|
|
if (mnemonic)
|
|
operand_ok = 0;
|
|
|
|
while (*l != END_OF_INSN)
|
|
{
|
|
/* Skip optional white space before operand. */
|
|
if (is_space_char (*l))
|
|
++l;
|
|
|
|
if (!is_operand_char (*l) && *l != END_OF_INSN)
|
|
{
|
|
as_bad (_("invalid character %c before operand %d"),
|
|
(char) (*l), i.operands + 1);
|
|
return NULL;
|
|
}
|
|
token_start = l;
|
|
|
|
paren_not_balanced = 0;
|
|
while (paren_not_balanced || *l != ',')
|
|
{
|
|
if (*l == END_OF_INSN)
|
|
{
|
|
if (paren_not_balanced)
|
|
{
|
|
as_bad (_("unbalanced brackets in operand %d."),
|
|
i.operands + 1);
|
|
return NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else if (!is_operand_char (*l) && !is_space_char (*l))
|
|
{
|
|
as_bad (_("invalid character %c in operand %d"),
|
|
(char) (*l), i.operands + 1);
|
|
return NULL;
|
|
}
|
|
if (*l == '[')
|
|
++paren_not_balanced;
|
|
if (*l == ']')
|
|
--paren_not_balanced;
|
|
l++;
|
|
}
|
|
|
|
if (l != token_start)
|
|
{
|
|
/* Yes, we've read in another operand. */
|
|
this_operand = i.operands++;
|
|
if (i.operands > MAX_OPERANDS)
|
|
{
|
|
as_bad (_("spurious operands; (%d operands/instruction max)"),
|
|
MAX_OPERANDS);
|
|
return NULL;
|
|
}
|
|
|
|
/* Now parse operand adding info to 'i' as we go along. */
|
|
END_STRING_AND_SAVE (l);
|
|
|
|
operand_ok = maxq20_operand (token_start);
|
|
|
|
RESTORE_END_STRING (l);
|
|
|
|
if (!operand_ok)
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
if (expecting_operand)
|
|
{
|
|
expecting_operand_after_comma:
|
|
as_bad (_("expecting operand after ','; got nothing"));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (*l == ',')
|
|
{
|
|
if (*(++l) == END_OF_INSN)
|
|
/* Just skip it, if it's \n complain. */
|
|
goto expecting_operand_after_comma;
|
|
|
|
expecting_operand = 1;
|
|
}
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
static int
|
|
match_operands (int type, MAX_ARG_TYPE flag_type, MAX_ARG_TYPE arg_type,
|
|
int op_num)
|
|
{
|
|
switch (type)
|
|
{
|
|
case REG:
|
|
if ((arg_type & A_REG) == A_REG)
|
|
return 1;
|
|
break;
|
|
case IMM:
|
|
if ((arg_type & A_IMM) == A_IMM)
|
|
return 1;
|
|
break;
|
|
case IMMBIT:
|
|
if ((arg_type & A_BIT_0) == A_BIT_0 && (i.maxq20_op[op_num].imms == 0))
|
|
return 1;
|
|
else if ((arg_type & A_BIT_1) == A_BIT_1
|
|
&& (i.maxq20_op[op_num].imms == 1))
|
|
return 1;
|
|
break;
|
|
case MEM:
|
|
if ((arg_type & A_MEM) == A_MEM)
|
|
return 1;
|
|
break;
|
|
|
|
case FLAG:
|
|
if ((arg_type & flag_type) == flag_type)
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case BIT:
|
|
if ((arg_type & ACC_BIT) == ACC_BIT && !strcmp (i.maxq20_op[op_num].r_bit->reg->reg_name, "ACC"))
|
|
return 1;
|
|
else if ((arg_type & SRC_BIT) == SRC_BIT && (op_num == 1))
|
|
return 1;
|
|
else if ((op_num == 0) && (arg_type & DST_BIT) == DST_BIT)
|
|
return 1;
|
|
break;
|
|
case DISP:
|
|
if ((arg_type & A_DISP) == A_DISP)
|
|
return 1;
|
|
case DATA:
|
|
if ((arg_type & A_DATA) == A_DATA)
|
|
return 1;
|
|
case BIT_BUCKET:
|
|
if ((arg_type & A_BIT_BUCKET) == A_BIT_BUCKET)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
match_template (void)
|
|
{
|
|
/* Points to template once we've found it. */
|
|
const MAXQ20_OPCODE_INFO *t;
|
|
char inv_oper;
|
|
inv_oper = 0;
|
|
|
|
for (t = current_templates->start; t < current_templates->end; t++)
|
|
{
|
|
/* Must have right number of operands. */
|
|
if (i.operands != t->op_number)
|
|
continue;
|
|
else if (!t->op_number)
|
|
break;
|
|
|
|
switch (i.operands)
|
|
{
|
|
case 2:
|
|
if (!match_operands (i.types[1], i.maxq20_op[1].flag, t->arg[1], 1))
|
|
{
|
|
inv_oper = 1;
|
|
continue;
|
|
}
|
|
case 1:
|
|
if (!match_operands (i.types[0], i.maxq20_op[0].flag, t->arg[0], 0))
|
|
{
|
|
inv_oper = 2;
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (t == current_templates->end)
|
|
{
|
|
/* We found no match. */
|
|
as_bad (_("operand %d is invalid for `%s'"),
|
|
inv_oper, current_templates->start->name);
|
|
return 0;
|
|
}
|
|
|
|
/* Copy the template we have found. */
|
|
i.op = *t;
|
|
return 1;
|
|
}
|
|
|
|
/* This function filters out the various combinations of operands which are
|
|
not allowed for a particular instruction. */
|
|
|
|
static int
|
|
match_filters (void)
|
|
{
|
|
/* Now we have at our disposal the instruction i. We will be using the
|
|
following fields i.op.name : This is the mnemonic name. i.types[2] :
|
|
These are the types of the operands (REG/IMM/DISP/MEM/BIT/FLAG/IMMBIT)
|
|
i.maxq20_op[2] : This contains the specific info of the operands. */
|
|
|
|
/* Our first filter : NO ALU OPERATIONS CAN HAVE THE ACTIVE ACCUMULATOR AS
|
|
SOURCE. */
|
|
if (!strcmp (i.op.name, "AND") || !strcmp (i.op.name, "OR")
|
|
|| !strcmp (i.op.name, "XOR") || !strcmp (i.op.name, "ADD")
|
|
|| !strcmp (i.op.name, "ADDC") || !strcmp (i.op.name, "SUB")
|
|
|| !strcmp (i.op.name, "SUBB"))
|
|
{
|
|
if (i.types[0] == REG)
|
|
{
|
|
if (i.maxq20_op[0].reg->Mod_name == 0xa)
|
|
{
|
|
as_bad (_
|
|
("The Accumulator cannot be used as a source in ALU instructions\n"));
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!strcmp (i.op.name, "MOVE") && (i.types[0] == MEM || i.types[1] == MEM)
|
|
&& i.operands == 2)
|
|
{
|
|
mem_access_syntax *mem_op = NULL;
|
|
|
|
if (i.types[0] == MEM)
|
|
{
|
|
mem_op =
|
|
(mem_access_syntax *) hash_find (mem_syntax_hash,
|
|
i.maxq20_op[0].mem->name);
|
|
if ((mem_op->type == SRC) && mem_op)
|
|
{
|
|
as_bad (_("'%s' operand cant be used as destination in %s"),
|
|
mem_op->name, i.op.name);
|
|
return 0;
|
|
}
|
|
else if ((mem_op->invalid_op != NULL) && (i.types[1] == MEM)
|
|
&& mem_op)
|
|
{
|
|
int k = 0;
|
|
|
|
for (k = 0; k < 5 || !mem_op->invalid_op[k]; k++)
|
|
{
|
|
if (mem_op->invalid_op[k] != NULL)
|
|
if (!strcmp
|
|
(mem_op->invalid_op[k], i.maxq20_op[1].mem->name))
|
|
{
|
|
as_bad (_
|
|
("Invalid Instruction '%s' operand cant be used with %s"),
|
|
mem_op->name, i.maxq20_op[1].mem->name);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i.types[1] == MEM)
|
|
{
|
|
mem_op = NULL;
|
|
mem_op =
|
|
(mem_access_syntax *) hash_find (mem_syntax_hash,
|
|
i.maxq20_op[1].mem->name);
|
|
if (mem_op->type == DST && mem_op)
|
|
{
|
|
as_bad (_("'%s' operand cant be used as source in %s"),
|
|
mem_op->name, i.op.name);
|
|
return 0;
|
|
}
|
|
else if (mem_op->invalid_op != NULL && i.types[0] == MEM && mem_op)
|
|
{
|
|
int k = 0;
|
|
|
|
for (k = 0; k < 5 || !mem_op->invalid_op[k]; k++)
|
|
{
|
|
if (mem_op->invalid_op[k] != NULL)
|
|
if (!strcmp
|
|
(mem_op->invalid_op[k], i.maxq20_op[0].mem->name))
|
|
{
|
|
as_bad (_
|
|
("Invalid Instruction '%s' operand cant be used with %s"),
|
|
mem_op->name, i.maxq20_op[0].mem->name);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else if (i.types[0] == REG
|
|
&& !strcmp (i.maxq20_op[0].reg->reg_name, "OFFS")
|
|
&& mem_op)
|
|
{
|
|
if (!strcmp (mem_op->name, "@BP[OFFS--]")
|
|
|| !strcmp (mem_op->name, "@BP[OFFS++]"))
|
|
{
|
|
as_bad (_
|
|
("Invalid Instruction '%s' operand cant be used with %s"),
|
|
mem_op->name, i.maxq20_op[0].mem->name);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Added for SRC and DST in one operand instructioni i.e OR @--DP[1] added
|
|
on 10-March-2004. */
|
|
if ((i.types[0] == MEM) && (i.operands == 1)
|
|
&& !(!strcmp (i.op.name, "POP") || !strcmp (i.op.name, "POPI")))
|
|
{
|
|
mem_access_syntax *mem_op = NULL;
|
|
|
|
if (i.types[0] == MEM)
|
|
{
|
|
mem_op =
|
|
(mem_access_syntax *) hash_find (mem_syntax_hash,
|
|
i.maxq20_op[0].mem->name);
|
|
if (mem_op->type == DST && mem_op)
|
|
{
|
|
as_bad (_("'%s' operand cant be used as source in %s"),
|
|
mem_op->name, i.op.name);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i.operands == 2 && i.types[0] == IMM)
|
|
{
|
|
as_bad (_("'%s' instruction cant have first operand as Immediate vale"),
|
|
i.op.name);
|
|
return 0;
|
|
}
|
|
|
|
/* Our second filter : SP or @SP-- cannot be used with PUSH or POP */
|
|
if (!strcmp (i.op.name, "PUSH") || !strcmp (i.op.name, "POP")
|
|
|| !strcmp (i.op.name, "POPI"))
|
|
{
|
|
if (i.types[0] == REG)
|
|
{
|
|
if (!strcmp (i.maxq20_op[0].reg->reg_name, "SP"))
|
|
{
|
|
as_bad (_("SP cannot be used with %s\n"), i.op.name);
|
|
return 0;
|
|
}
|
|
}
|
|
else if (i.types[0] == MEM
|
|
&& !strcmp (i.maxq20_op[0].mem->name, "@SP--"))
|
|
{
|
|
as_bad (_("@SP-- cannot be used with PUSH\n"));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* This filter checks that two memory references using DP's cannot be used
|
|
together in an instruction */
|
|
if (!strcmp (i.op.name, "MOVE") && i.mem_operands == 2)
|
|
{
|
|
if (strlen (i.maxq20_op[0].mem->name) != 6 ||
|
|
strcmp (i.maxq20_op[0].mem->name, i.maxq20_op[1].mem->name))
|
|
{
|
|
if (!strncmp (i.maxq20_op[0].mem->name, "@DP", 3)
|
|
&& !strncmp (i.maxq20_op[1].mem->name, "@DP", 3))
|
|
{
|
|
as_bad (_
|
|
("Operands either contradictory or use the data bus in read/write state together"));
|
|
return 0;
|
|
}
|
|
|
|
if (!strncmp (i.maxq20_op[0].mem->name, "@SP", 3)
|
|
&& !strncmp (i.maxq20_op[1].mem->name, "@SP", 3))
|
|
{
|
|
as_bad (_
|
|
("Operands either contradictory or use the data bus in read/write state together"));
|
|
return 0;
|
|
}
|
|
}
|
|
if ((i.maxq20_op[1].mem != NULL)
|
|
&& !strncmp (i.maxq20_op[1].mem->name, "NUL", 3))
|
|
{
|
|
as_bad (_("MOVE Cant Use NUL as SRC"));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* This filter checks that contradictory movement between DP register and
|
|
Memory access using DP followed by increment or decrement. */
|
|
|
|
if (!strcmp (i.op.name, "MOVE") && i.mem_operands == 1
|
|
&& i.reg_operands == 1)
|
|
{
|
|
int memnum, regnum;
|
|
|
|
memnum = (i.types[0] == MEM) ? 0 : 1;
|
|
regnum = (memnum == 0) ? 1 : 0;
|
|
if (!strncmp (i.maxq20_op[regnum].reg->reg_name, "DP", 2) &&
|
|
!strncmp ((i.maxq20_op[memnum].mem->name) + 1,
|
|
i.maxq20_op[regnum].reg->reg_name, 5)
|
|
&& strcmp ((i.maxq20_op[memnum].mem->name) + 1,
|
|
i.maxq20_op[regnum].reg->reg_name))
|
|
{
|
|
as_bad (_
|
|
("Contradictory movement between DP register and memory access using DP"));
|
|
return 0;
|
|
}
|
|
else if (!strcmp (i.maxq20_op[regnum].reg->reg_name, "SP") &&
|
|
!strncmp ((i.maxq20_op[memnum].mem->name) + 1,
|
|
i.maxq20_op[regnum].reg->reg_name, 2))
|
|
{
|
|
as_bad (_
|
|
("SP and @SP-- cannot be used together in a move instruction"));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* This filter restricts the instructions containing source and destination
|
|
bits to only CTRL module of the serial registers. Peripheral registers
|
|
yet to be defined. */
|
|
|
|
if (i.bit_operands == 1 && i.operands == 2)
|
|
{
|
|
int bitnum = (i.types[0] == BIT) ? 0 : 1;
|
|
|
|
if (strcmp (i.maxq20_op[bitnum].r_bit->reg->reg_name, "ACC"))
|
|
{
|
|
if (i.maxq20_op[bitnum].r_bit->reg->Mod_name >= 0x7 &&
|
|
i.maxq20_op[bitnum].r_bit->reg->Mod_name != CTRL)
|
|
{
|
|
as_bad (_
|
|
("Only Module 8 system registers allowed in this operation"));
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This filter is for checking the register bits. */
|
|
if (i.bit_operands == 1 || i.operands == 2)
|
|
{
|
|
int bitnum = 0, size = 0;
|
|
|
|
bitnum = (i.types[0] == BIT) ? 0 : 1;
|
|
if (i.bit_operands == 1)
|
|
{
|
|
switch (i.maxq20_op[bitnum].r_bit->reg->rtype)
|
|
{
|
|
case Reg_8W:
|
|
size = 7; /* 8 bit register, both read and write. */
|
|
break;
|
|
case Reg_16W:
|
|
size = 15;
|
|
break;
|
|
case Reg_8R:
|
|
size = 7;
|
|
if (bitnum == 0)
|
|
{
|
|
as_fatal (_("Read only Register used as destination"));
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case Reg_16R:
|
|
size = 15;
|
|
if (bitnum == 0)
|
|
{
|
|
as_fatal (_("Read only Register used as destination"));
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (size < (i.maxq20_op[bitnum].r_bit)->bit)
|
|
{
|
|
as_bad (_("Bit No '%d'exceeds register size in this operation"),
|
|
(i.maxq20_op[bitnum].r_bit)->bit);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (i.bit_operands == 2)
|
|
{
|
|
switch ((i.maxq20_op[0].r_bit)->reg->rtype)
|
|
{
|
|
case Reg_8W:
|
|
size = 7; /* 8 bit register, both read and write. */
|
|
break;
|
|
case Reg_16W:
|
|
size = 15;
|
|
break;
|
|
case Reg_8R:
|
|
case Reg_16R:
|
|
as_fatal (_("Read only Register used as destination"));
|
|
return 0;
|
|
}
|
|
|
|
if (size < (i.maxq20_op[0].r_bit)->bit)
|
|
{
|
|
as_bad (_
|
|
("Bit No '%d' exceeds register size in this operation"),
|
|
(i.maxq20_op[0].r_bit)->bit);
|
|
return 0;
|
|
}
|
|
|
|
size = 0;
|
|
switch ((i.maxq20_op[1].r_bit)->reg->rtype)
|
|
{
|
|
case Reg_8R:
|
|
case Reg_8W:
|
|
size = 7; /* 8 bit register, both read and write. */
|
|
break;
|
|
case Reg_16R:
|
|
case Reg_16W:
|
|
size = 15;
|
|
break;
|
|
}
|
|
|
|
if (size < (i.maxq20_op[1].r_bit)->bit)
|
|
{
|
|
as_bad (_
|
|
("Bit No '%d' exceeds register size in this operation"),
|
|
(i.maxq20_op[1].r_bit)->bit);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* No branch operations should occur into the data memory. Hence any memory
|
|
references have to be filtered out when used with instructions like
|
|
jump, djnz[] and call. */
|
|
|
|
if (!strcmp (i.op.name, "JUMP") || !strcmp (i.op.name, "CALL")
|
|
|| !strncmp (i.op.name, "DJNZ", 4))
|
|
{
|
|
if (i.mem_operands)
|
|
as_warn (_
|
|
("Memory References cannot be used with branching operations\n"));
|
|
}
|
|
|
|
if (!strcmp (i.op.name, "DJNZ"))
|
|
{
|
|
if (!
|
|
(strcmp (i.maxq20_op[0].reg->reg_name, "LC[0]")
|
|
|| strcmp (i.maxq20_op[0].reg->reg_name, "LC[1]")))
|
|
{
|
|
as_bad (_("DJNZ uses only LC[n] register \n"));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* No destination register used should be read only! */
|
|
if ((i.operands == 2 && i.types[0] == REG) || !strcmp (i.op.name, "POP")
|
|
|| !strcmp (i.op.name, "POPI"))
|
|
{ /* The destination is a register */
|
|
int regnum = 0;
|
|
|
|
if (!strcmp (i.op.name, "POP") || !strcmp (i.op.name, "POPI"))
|
|
{
|
|
regnum = 0;
|
|
|
|
if (i.types[regnum] == MEM)
|
|
{
|
|
mem_access_syntax *mem_op = NULL;
|
|
|
|
mem_op =
|
|
(mem_access_syntax *) hash_find (mem_syntax_hash,
|
|
i.maxq20_op[regnum].mem->
|
|
name);
|
|
if (mem_op->type == SRC && mem_op)
|
|
{
|
|
as_bad (_
|
|
("'%s' operand cant be used as destination in %s"),
|
|
mem_op->name, i.op.name);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i.maxq20_op[regnum].reg->rtype == Reg_8R
|
|
|| i.maxq20_op[regnum].reg->rtype == Reg_16R)
|
|
{
|
|
as_bad (_("Read only register used for writing purposes '%s'"),
|
|
i.maxq20_op[regnum].reg->reg_name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* While moving the address of a data in the data section, the destination
|
|
should be either data pointers only. */
|
|
if ((i.data_operands) && (i.operands == 2))
|
|
{
|
|
if ((i.types[0] != REG) && (i.types[0] != MEM))
|
|
{
|
|
as_bad (_("Invalid destination for this kind of source."));
|
|
return 0;
|
|
}
|
|
|
|
if (i.types[0] == REG && i.maxq20_op[0].reg->rtype == Reg_8W)
|
|
{
|
|
as_bad (_
|
|
("Invalid register as destination for this kind of source.Only data pointers can be used."));
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
decode_insn (void)
|
|
{
|
|
/* Check for the format Bit if defined. */
|
|
if (i.op.format == 0 || i.op.format == 1)
|
|
i.instr[0] = i.op.format << 7;
|
|
else
|
|
{
|
|
/* Format bit not defined. We will have to be find it out ourselves. */
|
|
if (i.imm_operands == 1 || i.data_operands == 1 || i.disp_operands == 1)
|
|
i.op.format = 0;
|
|
else
|
|
i.op.format = 1;
|
|
i.instr[0] = i.op.format << 7;
|
|
}
|
|
|
|
/* Now for the destination register. */
|
|
|
|
/* If destination register is already defined . The conditions are the
|
|
following: (1) The second entry in the destination array should be 0 (2)
|
|
If there are two operands then the first entry should not be a register,
|
|
memory or a register bit (3) If there are less than two operands and the
|
|
it is not a pop operation (4) The second argument is the carry
|
|
flag(applicable to move Acc.<b>,C. */
|
|
if (i.op.dst[1] == 0
|
|
&&
|
|
((i.types[0] != REG && i.types[0] != MEM && i.types[0] != BIT
|
|
&& i.operands == 2) || (i.operands < 2 && strcmp (i.op.name, "POP")
|
|
&& strcmp (i.op.name, "POPI"))
|
|
|| (i.op.arg[1] == FLAG_C)))
|
|
{
|
|
i.op.dst[0] &= 0x7f;
|
|
i.instr[0] |= i.op.dst[0];
|
|
}
|
|
else if (i.op.dst[1] == 0 && !strcmp (i.op.name, "DJNZ")
|
|
&&
|
|
(((i.types[0] == REG)
|
|
&& (!strcmp (i.maxq20_op[0].reg->reg_name, "LC[0]")
|
|
|| !strcmp (i.maxq20_op[0].reg->reg_name, "LC[1]")))))
|
|
{
|
|
i.op.dst[0] &= 0x7f;
|
|
if (!strcmp (i.maxq20_op[0].reg->reg_name, "LC[0]"))
|
|
i.instr[0] |= 0x4D;
|
|
|
|
if (!strcmp (i.maxq20_op[0].reg->reg_name, "LC[1]"))
|
|
i.instr[0] |= 0x5D;
|
|
}
|
|
else
|
|
{
|
|
unsigned char temp;
|
|
|
|
/* Target register will have to be specified. */
|
|
if (i.types[0] == REG
|
|
&& (i.op.dst[0] == REG || i.op.dst[0] == (REG | MEM)))
|
|
{
|
|
temp = (i.maxq20_op[0].reg)->opcode;
|
|
temp &= 0x7f;
|
|
i.instr[0] |= temp;
|
|
}
|
|
else if (i.types[0] == MEM && (i.op.dst[0] == (REG | MEM)))
|
|
{
|
|
temp = (i.maxq20_op[0].mem)->opcode;
|
|
temp &= 0x7f;
|
|
i.instr[0] |= temp;
|
|
}
|
|
else if (i.types[0] == BIT && (i.op.dst[0] == REG))
|
|
{
|
|
temp = (i.maxq20_op[0].r_bit)->reg->opcode;
|
|
temp &= 0x7f;
|
|
i.instr[0] |= temp;
|
|
}
|
|
else if (i.types[1] == BIT && (i.op.dst[0] == BIT))
|
|
{
|
|
temp = (i.maxq20_op[1].r_bit)->bit;
|
|
temp = temp << 4;
|
|
temp |= i.op.dst[1];
|
|
temp &= 0x7f;
|
|
i.instr[0] |= temp;
|
|
}
|
|
else
|
|
{
|
|
as_bad (_("Invalid Instruction"));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Now for the source register. */
|
|
|
|
/* If Source register is already known. The following conditions are
|
|
checked: (1) There are no operands (2) If there is only one operand and
|
|
it is a flag (3) If the operation is MOVE C,#0/#1 (4) If it is a POP
|
|
operation. */
|
|
|
|
if (i.operands == 0 || (i.operands == 1 && i.types[0] == FLAG)
|
|
|| (i.types[0] == FLAG && i.types[1] == IMMBIT)
|
|
|| !strcmp (i.op.name, "POP") || !strcmp (i.op.name, "POPI"))
|
|
i.instr[1] = i.op.src[0];
|
|
|
|
else if (i.imm_operands == 1 && ((i.op.src[0] & IMM) == IMM))
|
|
i.instr[1] = i.maxq20_op[this_operand].imms;
|
|
|
|
else if (i.types[this_operand] == REG && ((i.op.src[0] & REG) == REG))
|
|
i.instr[1] = (char) ((i.maxq20_op[this_operand].reg)->opcode);
|
|
|
|
else if (i.types[this_operand] == BIT && ((i.op.src[0] & REG) == REG))
|
|
i.instr[1] = (char) (i.maxq20_op[this_operand].r_bit->reg->opcode);
|
|
|
|
else if (i.types[this_operand] == MEM && ((i.op.src[0] & MEM) == MEM))
|
|
i.instr[1] = (char) ((i.maxq20_op[this_operand].mem)->opcode);
|
|
|
|
else if (i.types[this_operand] == DATA && ((i.op.src[0] & DATA) == DATA))
|
|
/* This will copy only the lower order bytes into the instruction. The
|
|
higher order bytes have already been copied into the prefix register. */
|
|
i.instr[1] = 0;
|
|
|
|
/* Decoding the source in the case when the second array entry is not 0.
|
|
This means that the source register has been divided into two nibbles. */
|
|
|
|
else if (i.op.src[1] != 0)
|
|
{
|
|
/* If the first operand is a accumulator bit then
|
|
the first 4 bits will be filled with the bit number. */
|
|
if (i.types[0] == BIT && ((i.op.src[0] & BIT) == BIT))
|
|
{
|
|
unsigned char temp = (i.maxq20_op[0].r_bit)->bit;
|
|
|
|
temp = temp << 4;
|
|
temp |= i.op.src[1];
|
|
i.instr[1] = temp;
|
|
}
|
|
/* In case of MOVE dst.<b>,#1 The first nibble in the source register
|
|
has to start with a zero. This is called a ZEROBIT */
|
|
else if (i.types[0] == BIT && ((i.op.src[0] & ZEROBIT) == ZEROBIT))
|
|
{
|
|
char temp = (i.maxq20_op[0].r_bit)->bit;
|
|
|
|
temp = temp << 4;
|
|
temp |= i.op.src[1];
|
|
temp &= 0x7f;
|
|
i.instr[1] = temp;
|
|
}
|
|
/* Similarly for a ONEBIT */
|
|
else if (i.types[0] == BIT && ((i.op.src[0] & ONEBIT) == ONEBIT))
|
|
{
|
|
char temp = (i.maxq20_op[0].r_bit)->bit;
|
|
|
|
temp = temp << 4;
|
|
temp |= i.op.src[1];
|
|
temp |= 0x80;
|
|
i.instr[1] = temp;
|
|
}
|
|
/* In case the second operand is a register bit (MOVE C,Acc.<b> or MOVE
|
|
C,src.<b> */
|
|
else if (i.types[1] == BIT)
|
|
{
|
|
if (i.op.src[1] == 0 && i.op.src[1] == REG)
|
|
i.instr[1] = (i.maxq20_op[1].r_bit)->reg->opcode;
|
|
|
|
else if (i.op.src[0] == BIT && i.op.src)
|
|
{
|
|
char temp = (i.maxq20_op[1].r_bit)->bit;
|
|
|
|
temp = temp << 4;
|
|
temp |= i.op.src[1];
|
|
i.instr[1] = temp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
as_bad (_("Invalid Instruction"));
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* This is a function for outputting displacement operands. */
|
|
|
|
static void
|
|
output_disp (fragS *insn_start_frag, offsetT insn_start_off)
|
|
{
|
|
char *p;
|
|
relax_substateT subtype;
|
|
symbolS *sym;
|
|
offsetT off;
|
|
int diff;
|
|
|
|
diff = 0;
|
|
insn_start_frag = frag_now;
|
|
insn_start_off = frag_now_fix ();
|
|
|
|
switch (i.Instr_Prefix)
|
|
{
|
|
case LONG_PREFIX:
|
|
subtype = EXPLICT_LONG_PREFIX;
|
|
break;
|
|
case SHORT_PREFIX:
|
|
subtype = SHORT_PREFIX;
|
|
break;
|
|
default:
|
|
subtype = NO_PREFIX;
|
|
break;
|
|
}
|
|
|
|
/* Its a symbol. Here we end the frag and start the relaxation. Now in our
|
|
case there is no need for relaxation. But we do need support for a
|
|
prefix operator. Hence we will check whethere is room for 4 bytes ( 2
|
|
for prefix + 2 for the current instruction ) Hence if at a particular
|
|
time we find out whether the prefix operator is reqd , we shift the
|
|
current instruction two places ahead and insert the prefix instruction. */
|
|
frag_grow (2 + 2);
|
|
p = frag_more (2);
|
|
|
|
sym = i.maxq20_op[this_operand].disps->X_add_symbol;
|
|
off = i.maxq20_op[this_operand].disps->X_add_number;
|
|
|
|
if (i.maxq20_op[this_operand].disps->X_add_symbol != NULL && sym && frag_now
|
|
&& (subtype != EXPLICT_LONG_PREFIX))
|
|
{
|
|
/* If in the same frag. */
|
|
if (frag_now == symbol_get_frag (sym))
|
|
{
|
|
diff =
|
|
((((expressionS *) symbol_get_value_expression (sym))->
|
|
X_add_number) - insn_start_off);
|
|
|
|
/* PC points to the next instruction. */
|
|
diff = (diff / MAXQ_OCTETS_PER_BYTE) - 1;
|
|
|
|
if (diff >= -128 && diff <= 127)
|
|
{
|
|
i.instr[1] = (char) diff;
|
|
|
|
/* This will be overwritten later when the symbol is resolved. */
|
|
*p = i.instr[1];
|
|
*(p + 1) = i.instr[0];
|
|
|
|
/* No Need to create a FIXUP. */
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This will be overwritten later when the symbol is resolved. */
|
|
*p = i.instr[1];
|
|
*(p + 1) = i.instr[0];
|
|
|
|
if (i.maxq20_op[this_operand].disps->X_op != O_constant
|
|
&& i.maxq20_op[this_operand].disps->X_op != O_symbol)
|
|
{
|
|
/* Handle complex expressions. */
|
|
sym = make_expr_symbol (i.maxq20_op[this_operand].disps);
|
|
off = 0;
|
|
}
|
|
|
|
/* Vineet : This has been added for md_estimate_size_before_relax to
|
|
estimate the correct size. */
|
|
if (subtype != SHORT_PREFIX)
|
|
i.reloc[this_operand] = LONG_PREFIX;
|
|
|
|
frag_var (rs_machine_dependent, 2, i.reloc[this_operand], subtype, sym, off, p);
|
|
}
|
|
|
|
/* This is a function for outputting displacement operands. */
|
|
|
|
static void
|
|
output_data (fragS *insn_start_frag, offsetT insn_start_off)
|
|
{
|
|
char *p;
|
|
relax_substateT subtype;
|
|
symbolS *sym;
|
|
offsetT off;
|
|
int diff;
|
|
|
|
diff = 0;
|
|
off = 0;
|
|
insn_start_frag = frag_now;
|
|
insn_start_off = frag_now_fix ();
|
|
|
|
subtype = EXPLICT_LONG_PREFIX;
|
|
|
|
frag_grow (2 + 2);
|
|
p = frag_more (2);
|
|
|
|
sym = i.maxq20_op[this_operand].data;
|
|
off = 0;
|
|
|
|
/* This will be overwritten later when the symbol is resolved. */
|
|
*p = i.instr[1];
|
|
*(p + 1) = i.instr[0];
|
|
|
|
if (i.maxq20_op[this_operand].disps->X_op != O_constant
|
|
&& i.maxq20_op[this_operand].disps->X_op != O_symbol)
|
|
/* Handle complex expressions. */
|
|
/* Because data is already in terms of symbol so no
|
|
need to convert it from expression to symbol. */
|
|
off = 0;
|
|
|
|
frag_var (rs_machine_dependent, 2, i.reloc[this_operand], subtype, sym, off, p);
|
|
}
|
|
|
|
static void
|
|
output_insn (void)
|
|
{
|
|
fragS *insn_start_frag;
|
|
offsetT insn_start_off;
|
|
char *p;
|
|
|
|
/* Tie dwarf2 debug info to the address at the start of the insn. We can't
|
|
do this after the insn has been output as the current frag may have been
|
|
closed off. eg. by frag_var. */
|
|
dwarf2_emit_insn (0);
|
|
|
|
/* To ALign the text section on word. */
|
|
|
|
frag_align (1, 0, 1);
|
|
|
|
/* We initialise the frags for this particular instruction. */
|
|
insn_start_frag = frag_now;
|
|
insn_start_off = frag_now_fix ();
|
|
|
|
/* If there are displacement operators(unresolved) present, then handle
|
|
them separately. */
|
|
if (i.disp_operands)
|
|
{
|
|
output_disp (insn_start_frag, insn_start_off);
|
|
return;
|
|
}
|
|
|
|
if (i.data_operands)
|
|
{
|
|
output_data (insn_start_frag, insn_start_off);
|
|
return;
|
|
}
|
|
|
|
/* Check whether the INSERT_BUFFER has to be written. */
|
|
if (strcmp (INSERT_BUFFER, ""))
|
|
{
|
|
p = frag_more (2);
|
|
|
|
*p++ = INSERT_BUFFER[1];
|
|
*p = INSERT_BUFFER[0];
|
|
}
|
|
|
|
/* Check whether the prefix instruction has to be written. */
|
|
if (strcmp (PFX_INSN, ""))
|
|
{
|
|
p = frag_more (2);
|
|
|
|
*p++ = PFX_INSN[1];
|
|
*p = PFX_INSN[0];
|
|
}
|
|
|
|
p = frag_more (2);
|
|
/* For Little endian. */
|
|
*p++ = i.instr[1];
|
|
*p = i.instr[0];
|
|
}
|
|
|
|
static void
|
|
make_new_reg_table (void)
|
|
{
|
|
unsigned long size_pm = sizeof (peripheral_reg_table);
|
|
num_of_reg = ARRAY_SIZE (peripheral_reg_table);
|
|
|
|
new_reg_table = xmalloc (size_pm);
|
|
if (new_reg_table == NULL)
|
|
as_bad (_("Cannot allocate memory"));
|
|
|
|
memcpy (new_reg_table, peripheral_reg_table, size_pm);
|
|
}
|
|
|
|
/* pmmain performs the initilizations for the pheripheral modules. */
|
|
|
|
static void
|
|
pmmain (void)
|
|
{
|
|
make_new_reg_table ();
|
|
return;
|
|
}
|
|
|
|
void
|
|
md_begin (void)
|
|
{
|
|
const char *hash_err = NULL;
|
|
int c = 0;
|
|
char *p;
|
|
const MAXQ20_OPCODE_INFO *optab;
|
|
MAXQ20_OPCODES *core_optab; /* For opcodes of the same name. This will
|
|
be inserted into the hash table. */
|
|
struct reg *reg_tab;
|
|
struct mem_access_syntax const *memsyntab;
|
|
struct mem_access *memtab;
|
|
struct bit_name *bittab;
|
|
|
|
/* Initilize pherioipheral modules. */
|
|
pmmain ();
|
|
|
|
/* Initialise the opcode hash table. */
|
|
op_hash = hash_new ();
|
|
|
|
optab = op_table; /* Initialise it to the first entry of the
|
|
maxq20 operand table. */
|
|
|
|
/* Setup for loop. */
|
|
core_optab = xmalloc (sizeof (MAXQ20_OPCODES));
|
|
core_optab->start = optab;
|
|
|
|
while (1)
|
|
{
|
|
++optab;
|
|
if (optab->name == NULL || strcmp (optab->name, (optab - 1)->name) != 0)
|
|
{
|
|
/* different name --> ship out current template list; add to hash
|
|
table; & begin anew. */
|
|
|
|
core_optab->end = optab;
|
|
#ifdef MAXQ10S
|
|
if (max_version == bfd_mach_maxq10)
|
|
{
|
|
if (((optab - 1)->arch == MAXQ10) || ((optab - 1)->arch == MAX))
|
|
{
|
|
hash_err = hash_insert (op_hash,
|
|
(optab - 1)->name,
|
|
(void *) core_optab);
|
|
}
|
|
}
|
|
else if (max_version == bfd_mach_maxq20)
|
|
{
|
|
if (((optab - 1)->arch == MAXQ20) || ((optab - 1)->arch == MAX))
|
|
{
|
|
#endif
|
|
hash_err = hash_insert (op_hash,
|
|
(optab - 1)->name,
|
|
(void *) core_optab);
|
|
#if MAXQ10S
|
|
}
|
|
}
|
|
else
|
|
as_fatal (_("Internal Error: Illegal Architecure specified"));
|
|
#endif
|
|
if (hash_err)
|
|
as_fatal (_("Internal Error: Can't hash %s: %s"),
|
|
(optab - 1)->name, hash_err);
|
|
|
|
if (optab->name == NULL)
|
|
break;
|
|
core_optab = xmalloc (sizeof (MAXQ20_OPCODES));
|
|
core_optab->start = optab;
|
|
}
|
|
}
|
|
|
|
/* Initialise a new register table. */
|
|
reg_hash = hash_new ();
|
|
|
|
for (reg_tab = system_reg_table;
|
|
reg_tab < (system_reg_table + ARRAY_SIZE (system_reg_table));
|
|
reg_tab++)
|
|
{
|
|
#if MAXQ10S
|
|
switch (max_version)
|
|
{
|
|
case bfd_mach_maxq10:
|
|
if ((reg_tab->arch == MAXQ10) || (reg_tab->arch == MAX))
|
|
hash_err = hash_insert (reg_hash, reg_tab->reg_name, (void *) reg_tab);
|
|
break;
|
|
|
|
case bfd_mach_maxq20:
|
|
if ((reg_tab->arch == MAXQ20) || (reg_tab->arch == MAX))
|
|
{
|
|
#endif
|
|
hash_err =
|
|
hash_insert (reg_hash, reg_tab->reg_name, (void *) reg_tab);
|
|
#if MAXQ10S
|
|
}
|
|
break;
|
|
default:
|
|
as_fatal (_("Invalid architecture type"));
|
|
}
|
|
#endif
|
|
|
|
if (hash_err)
|
|
as_fatal (_("Internal Error : Can't Hash %s : %s"),
|
|
reg_tab->reg_name, hash_err);
|
|
}
|
|
|
|
/* Pheripheral Registers Entry. */
|
|
for (reg_tab = new_reg_table;
|
|
reg_tab < (new_reg_table + num_of_reg - 1); reg_tab++)
|
|
{
|
|
hash_err = hash_insert (reg_hash, reg_tab->reg_name, (void *) reg_tab);
|
|
|
|
if (hash_err)
|
|
as_fatal (_("Internal Error : Can't Hash %s : %s"),
|
|
reg_tab->reg_name, hash_err);
|
|
}
|
|
|
|
/* Initialise a new memory operand table. */
|
|
mem_hash = hash_new ();
|
|
|
|
for (memtab = mem_table;
|
|
memtab < mem_table + ARRAY_SIZE (mem_table);
|
|
memtab++)
|
|
{
|
|
hash_err = hash_insert (mem_hash, memtab->name, (void *) memtab);
|
|
if (hash_err)
|
|
as_fatal (_("Internal Error : Can't Hash %s : %s"),
|
|
memtab->name, hash_err);
|
|
}
|
|
|
|
bit_hash = hash_new ();
|
|
|
|
for (bittab = bit_table;
|
|
bittab < bit_table + ARRAY_SIZE (bit_table);
|
|
bittab++)
|
|
{
|
|
hash_err = hash_insert (bit_hash, bittab->name, (void *) bittab);
|
|
if (hash_err)
|
|
as_fatal (_("Internal Error : Can't Hash %s : %s"),
|
|
bittab->name, hash_err);
|
|
}
|
|
|
|
mem_syntax_hash = hash_new ();
|
|
|
|
for (memsyntab = mem_access_syntax_table;
|
|
memsyntab < mem_access_syntax_table + ARRAY_SIZE (mem_access_syntax_table);
|
|
memsyntab++)
|
|
{
|
|
hash_err =
|
|
hash_insert (mem_syntax_hash, memsyntab->name, (void *) memsyntab);
|
|
if (hash_err)
|
|
as_fatal (_("Internal Error : Can't Hash %s : %s"),
|
|
memsyntab->name, hash_err);
|
|
}
|
|
|
|
/* Initialise the lexical tables,mnemonic chars,operand chars. */
|
|
for (c = 0; c < 256; c++)
|
|
{
|
|
if (ISDIGIT (c))
|
|
{
|
|
digit_chars[c] = c;
|
|
mnemonic_chars[c] = c;
|
|
operand_chars[c] = c;
|
|
register_chars[c] = c;
|
|
}
|
|
else if (ISLOWER (c))
|
|
{
|
|
mnemonic_chars[c] = c;
|
|
operand_chars[c] = c;
|
|
register_chars[c] = c;
|
|
}
|
|
else if (ISUPPER (c))
|
|
{
|
|
mnemonic_chars[c] = TOLOWER (c);
|
|
register_chars[c] = c;
|
|
operand_chars[c] = c;
|
|
}
|
|
|
|
if (ISALPHA (c) || ISDIGIT (c))
|
|
{
|
|
identifier_chars[c] = c;
|
|
}
|
|
else if (c > 128)
|
|
{
|
|
identifier_chars[c] = c;
|
|
operand_chars[c] = c;
|
|
}
|
|
}
|
|
|
|
/* All the special characters. */
|
|
register_chars['@'] = '@';
|
|
register_chars['+'] = '+';
|
|
register_chars['-'] = '-';
|
|
digit_chars['-'] = '-';
|
|
identifier_chars['_'] = '_';
|
|
identifier_chars['.'] = '.';
|
|
register_chars['['] = '[';
|
|
register_chars[']'] = ']';
|
|
operand_chars['_'] = '_';
|
|
operand_chars['#'] = '#';
|
|
mnemonic_chars['['] = '[';
|
|
mnemonic_chars[']'] = ']';
|
|
|
|
for (p = operand_special_chars; *p != '\0'; p++)
|
|
operand_chars[(unsigned char) *p] = (unsigned char) *p;
|
|
|
|
/* Set the maxq arch type. */
|
|
maxq_target (max_version);
|
|
}
|
|
|
|
/* md_assemble - Parse Instr - Seprate menmonics and operands - lookup the
|
|
menmunonic in the operand table - Parse operands and populate the
|
|
structure/template - Match the operand with opcode and its validity -
|
|
Output Instr. */
|
|
|
|
void
|
|
md_assemble (char *line)
|
|
{
|
|
int j;
|
|
|
|
char mnemonic[MAX_MNEM_SIZE];
|
|
char temp4prev[256];
|
|
static char prev_insn[256];
|
|
|
|
/* Initialize globals. */
|
|
memset (&i, '\0', sizeof (i));
|
|
for (j = 0; j < MAX_OPERANDS; j++)
|
|
i.reloc[j] = NO_RELOC;
|
|
|
|
i.prefix = -1;
|
|
PFX_INSN[0] = 0;
|
|
PFX_INSN[1] = 0;
|
|
INSERT_BUFFER[0] = 0;
|
|
INSERT_BUFFER[1] = 0;
|
|
|
|
memcpy (temp4prev, line, strlen (line) + 1);
|
|
|
|
save_stack_p = save_stack;
|
|
|
|
line = (char *) parse_insn (line, mnemonic);
|
|
if (line == NULL)
|
|
return;
|
|
|
|
line = (char *) parse_operands (line, mnemonic);
|
|
if (line == NULL)
|
|
return;
|
|
|
|
/* Next, we find a template that matches the given insn, making sure the
|
|
overlap of the given operands types is consistent with the template
|
|
operand types. */
|
|
if (!match_template ())
|
|
return;
|
|
|
|
/* In the MAXQ20, there are certain register combinations, and other
|
|
restrictions which are not allowed. We will try to resolve these right
|
|
now. */
|
|
if (!match_filters ())
|
|
return;
|
|
|
|
/* Check for the appropriate PFX register. */
|
|
set_prefix ();
|
|
pfx_for_imm_val (0);
|
|
|
|
if (!decode_insn ()) /* decode insn. */
|
|
need_pass_2 = 1;
|
|
|
|
/* Check for Exlipct PFX instruction. */
|
|
if (PFX_INSN[0] && (strstr (prev_insn, "PFX") || strstr (prev_insn, "pfx")))
|
|
as_warn (_("Ineffective insntruction %s \n"), prev_insn);
|
|
|
|
memcpy (prev_insn, temp4prev, strlen (temp4prev) + 1);
|
|
|
|
/* We are ready to output the insn. */
|
|
output_insn ();
|
|
}
|