2731 lines
82 KiB
C
2731 lines
82 KiB
C
/* Print E2K instructions.
|
|
Copyright (C) 1989-2016 Free Software Foundation, Inc.
|
|
|
|
This file is part of the GNU opcodes library.
|
|
|
|
This library 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.
|
|
|
|
It 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 this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
/* This is a concatenation of e2k-opc.c and e2k-dis.c */
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "disas/dis-asm.h"
|
|
#include "e2k.h"
|
|
|
|
/* I need MCPU referred to in "e2k-opc.h" here to prevent "e2k-opc.c" from
|
|
being linked into OBJDUMP. At the same time I want to prevent "e2k-dis.c"
|
|
from being mistakenly linked into GAS because of this variable, which is why
|
|
it's made static and renamed here. */
|
|
#define mcpu e2k_dis_mcpu
|
|
static unsigned long mcpu;
|
|
|
|
typedef struct e2k_opcode_hash
|
|
{
|
|
struct e2k_opcode_hash *next;
|
|
const e2k_alf_opcode_templ *templ;
|
|
} e2k_opcode_hash;
|
|
|
|
/* Use opcode as a hash key for now. */
|
|
static e2k_opcode_hash *opcode_hash_table[128];
|
|
|
|
/* At STAGE = 0 the hash table's buffer of an appropriate size is allocated,
|
|
at STAGE = 1 it is filled in. */
|
|
static int build_hash_table_stage = 0;
|
|
static int num_opcodes;
|
|
static e2k_opcode_hash *hash_buf = NULL;
|
|
|
|
static void
|
|
add_to_insn_table (e2k_opcode_templ *t)
|
|
{
|
|
const e2k_alf_opcode_templ *alf;
|
|
e2k_opcode_hash *h;
|
|
|
|
if (build_hash_table_stage == 0)
|
|
{
|
|
num_opcodes++;
|
|
return;
|
|
}
|
|
|
|
h = &hash_buf[num_opcodes++];
|
|
alf = (const e2k_alf_opcode_templ *) t;
|
|
|
|
assert (alf->opc < 128);
|
|
|
|
h->next = opcode_hash_table[alf->opc];
|
|
h->templ = alf;
|
|
opcode_hash_table[alf->opc] = h;
|
|
}
|
|
|
|
static const char *
|
|
merge_alopf1_with_alopf11 (e2k_alf1_opcode_templ *l,
|
|
e2k_alopf11_opcode_templ *r)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
if (l->allowed_channels[i] != 0)
|
|
{
|
|
if (r->allowed_channels[i] != 0)
|
|
abort ();
|
|
|
|
r->allowed_channels[i] = l->allowed_channels[i];
|
|
/* This value seems to be not currently reserved in iset. Use it to
|
|
specify that an instruction in the related ALC should be actually
|
|
encoded as ALOPF1, not ALOPF11. */
|
|
r->ales_opc2[i] = 0xff;
|
|
}
|
|
}
|
|
|
|
/* Signalize to the caller that the hash entry should be replaced with more
|
|
"universal" merged ALOPF11. */
|
|
return "jam";
|
|
}
|
|
|
|
static const char *
|
|
merge_alopf11 (struct e2k_opcode_templ *lhs,
|
|
const struct e2k_opcode_templ *rhs)
|
|
{
|
|
int i;
|
|
e2k_alopf11_opcode_templ *l = (e2k_alopf11_opcode_templ *) lhs;
|
|
const e2k_alopf11_opcode_templ *r = (const e2k_alopf11_opcode_templ *) rhs;
|
|
|
|
/* FIXME: accessing `alopf' field in LHS which may very well turn out to be
|
|
ALOPF1 via `e2k_alopf11_opcode_templ *' is controversial. It'd be be better
|
|
to use `e2k_alf_opcode_templ *'. */
|
|
if (l->alopf == ALOPF1 && r->alopf == ALOPF11)
|
|
{
|
|
e2k_alf1_opcode_templ *alopf1 = (e2k_alf1_opcode_templ *) lhs;
|
|
e2k_alopf11_opcode_templ *alopf11 = (e2k_alopf11_opcode_templ *) rhs;
|
|
return merge_alopf1_with_alopf11 (alopf1, alopf11);
|
|
}
|
|
|
|
if (l->alopf != r->alopf
|
|
|| (l->alopf != ALOPF11 && l->alopf != ALOPF11_LIT8))
|
|
abort ();
|
|
|
|
if (l->opc != r->opc)
|
|
abort ();
|
|
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
if (r->allowed_channels[i] != 0)
|
|
{
|
|
if (l->allowed_channels[i] != 0)
|
|
{
|
|
if (l->ales_opc2[i] != r->ales_opc2[i])
|
|
abort ();
|
|
}
|
|
else
|
|
{
|
|
l->allowed_channels[i] = r->allowed_channels[i];
|
|
l->ales_opc2[i] = r->ales_opc2[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return NULL by analogy with succesfull hash_jam to make it clear to the
|
|
caller that there's no point in replacing the hash entry. */
|
|
return NULL;
|
|
}
|
|
|
|
static const char *
|
|
merge_alopf_simple (struct e2k_opcode_templ *lhs,
|
|
const struct e2k_opcode_templ *rhs)
|
|
{
|
|
int i;
|
|
e2k_alf_opcode_templ *l = (e2k_alf_opcode_templ *) lhs;
|
|
const e2k_alf_opcode_templ *r = (const e2k_alf_opcode_templ *) rhs;
|
|
|
|
if ((l->alopf != ALOPF1 && l->alopf != ALOPF2 && l->alopf != ALOPF21)
|
|
|| l->alopf != r->alopf)
|
|
abort ();
|
|
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
if (r->allowed_channels[i] != 0)
|
|
{
|
|
/* There should be no duplication of ALC'es now. I can probably rely
|
|
on this above in `merge_alopf11 ()' as well . . . */
|
|
if (l->allowed_channels[i] != 0)
|
|
abort ();
|
|
else
|
|
l->allowed_channels[i] = r->allowed_channels[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
init_opcode_templs (void)
|
|
{
|
|
#if 0
|
|
init_opcode_templs_from_table (0, e2k_short_opers, sizeof (e2k_short_opers) / sizeof (e2k_short_opers[0]));
|
|
init_opcode_templs_from_table (1, e2k_long_opers, sizeof (e2k_long_opers) / sizeof (e2k_long_opers[0]));
|
|
#endif /* 0 */
|
|
|
|
/* Opcodes are mostly added from an automatically generated e2k-opc.h */
|
|
#include "e2k-opc.h"
|
|
|
|
/* Add some opcodes manually. */
|
|
|
|
#define E2K_OPCODE_VEC_ENTRY(name, parse_fn) \
|
|
{ \
|
|
static const e2k_opcode_templ dummy = {#name, parse_fn, NULL}; \
|
|
add_to_insn_table ((e2k_opcode_templ *) &dummy); \
|
|
}
|
|
|
|
#define COPF2_VEC_ENTRY(name, ctp_opc, allowed_ctprs, label_expected) \
|
|
{ \
|
|
static const e2k_copf2_opcode_templ dummy = \
|
|
{#name, parse_copf2_args, NULL, ctp_opc, allowed_ctprs, \
|
|
label_expected}; \
|
|
\
|
|
add_to_insn_table ((e2k_opcode_templ *) &dummy); \
|
|
}
|
|
|
|
#define COPF4_VEC_ENTRY(name) \
|
|
{ \
|
|
static const e2k_opcode_templ dummy = \
|
|
{#name, parse_copf4_args, NULL}; \
|
|
\
|
|
add_to_insn_table ((e2k_opcode_templ *) &dummy); \
|
|
}
|
|
|
|
|
|
#define AAOPF1_VEC_ENTRY(name, opc, format) \
|
|
{ \
|
|
static e2k_mova_opcode_templ dummy = \
|
|
{#name, parse_mova_args, NULL, opc, format}; \
|
|
\
|
|
add_to_insn_table ((e2k_opcode_templ *) &dummy); \
|
|
}
|
|
|
|
/* FIXME: stupidly adding an extra cast to `(void *)' to avoid strict
|
|
aliasing warnings when compiling with `-O2'. */
|
|
#define SETCMD_VEC_ENTRY(name, id) \
|
|
{ \
|
|
static const e2k_setcmd_opcode_templ dummy = \
|
|
{#name,parse_setcmd_args, NULL, id}; \
|
|
\
|
|
add_to_insn_table ((e2k_opcode_templ *) (void *) &dummy); \
|
|
}
|
|
|
|
COPF2_VEC_ENTRY (disp, 0x0, ALL_CTPRS, EXPECT_LABEL);
|
|
COPF2_VEC_ENTRY (ldisp, 0x1, CTPR2, EXPECT_LABEL);
|
|
/* FIXME: unlike DISP only a numeric value should be
|
|
allowed for SDISP, a symbol makes no sense. */
|
|
COPF2_VEC_ENTRY (sdisp, 0x2, ALL_CTPRS, EXPECT_LABEL);
|
|
|
|
|
|
/* FIXME: the underlying two entries are COPF1 in fact. */
|
|
COPF2_VEC_ENTRY (return, 0x3, CTPR3, NO_LABEL);
|
|
if (mcpu >= 3)
|
|
COPF2_VEC_ENTRY (gettsd, 0x3, ALL_CTPRS, NO_LABEL);
|
|
|
|
E2K_OPCODE_VEC_ENTRY (pref, parse_pref_args);
|
|
|
|
COPF4_VEC_ENTRY (flushr);
|
|
COPF4_VEC_ENTRY (flushc);
|
|
|
|
SETCMD_VEC_ENTRY (setbn, 0);
|
|
SETCMD_VEC_ENTRY (setbp, 1);
|
|
SETCMD_VEC_ENTRY (settr, 2);
|
|
SETCMD_VEC_ENTRY (setwd, 3);
|
|
SETCMD_VEC_ENTRY (vfrpsz, 4);
|
|
SETCMD_VEC_ENTRY (setei, 5);
|
|
|
|
if (mcpu >= 2)
|
|
E2K_OPCODE_VEC_ENTRY (setsft, parse_setsft_args);
|
|
|
|
E2K_OPCODE_VEC_ENTRY (nop, parse_nop_args);
|
|
E2K_OPCODE_VEC_ENTRY (ct, parse_ct_args);
|
|
E2K_OPCODE_VEC_ENTRY (call, parse_ct_args);
|
|
|
|
if (mcpu >= 6)
|
|
E2K_OPCODE_VEC_ENTRY (hcall, parse_hcall_args);
|
|
|
|
E2K_OPCODE_VEC_ENTRY (ipd, parse_ipd_args);
|
|
|
|
E2K_OPCODE_VEC_ENTRY (loop_mode, parse_loop_mode_args);
|
|
E2K_OPCODE_VEC_ENTRY (alc, parse_alc_args);
|
|
E2K_OPCODE_VEC_ENTRY (abn, parse_abn_args);
|
|
E2K_OPCODE_VEC_ENTRY (abp, parse_abp_args);
|
|
E2K_OPCODE_VEC_ENTRY (abg, parse_abg_args);
|
|
E2K_OPCODE_VEC_ENTRY (bap, parse_bap_args);
|
|
E2K_OPCODE_VEC_ENTRY (eap, parse_eap_args);
|
|
|
|
E2K_OPCODE_VEC_ENTRY (pass, parse_pass_args);
|
|
E2K_OPCODE_VEC_ENTRY (andp, parse_andp_args);
|
|
E2K_OPCODE_VEC_ENTRY (landp, parse_landp_args);
|
|
|
|
E2K_OPCODE_VEC_ENTRY (ibranch, parse_ibranch_args);
|
|
E2K_OPCODE_VEC_ENTRY (rbranch, parse_ibranch_args);
|
|
E2K_OPCODE_VEC_ENTRY (done, parse_done_hret_glaunch_args);
|
|
|
|
if (mcpu >= 6)
|
|
{
|
|
E2K_OPCODE_VEC_ENTRY (hret, parse_done_hret_glaunch_args);
|
|
E2K_OPCODE_VEC_ENTRY (glaunch, parse_done_hret_glaunch_args);
|
|
}
|
|
|
|
E2K_OPCODE_VEC_ENTRY (wait, parse_wait_args);
|
|
|
|
{
|
|
static const e2k_alf3_opcode_templ mmurr =
|
|
{"mmurr", parse_alf_args, NULL, MMURR, NO_MAS, 0x67, {0, 0, 1, 0, 0, 1},
|
|
ARGS_DDD};
|
|
|
|
/* FIXME: I've intentionally specified NO_MAS here so that `parse_alf_args
|
|
()' doesn't attempt to parse it. I set it manually in parse_mmurw_args
|
|
instead. */
|
|
static const e2k_alf3_opcode_templ mmurw =
|
|
{"mmurw", parse_alf_args, NULL, MMURW, NO_MAS, 0x27, {0, 0, 1, 0, 0, 0},
|
|
ARGS_DDD};
|
|
|
|
add_to_insn_table ((e2k_opcode_templ *) &mmurr);
|
|
add_to_insn_table ((e2k_opcode_templ *) &mmurw);
|
|
}
|
|
|
|
E2K_OPCODE_VEC_ENTRY (incr, parse_incr_args);
|
|
|
|
AAOPF1_VEC_ENTRY (movab, 0x1, ARGS_S);
|
|
AAOPF1_VEC_ENTRY (movah, 0x2, ARGS_S);
|
|
AAOPF1_VEC_ENTRY (movaw, 0x3, ARGS_S);
|
|
AAOPF1_VEC_ENTRY (movad, 0x4, ARGS_D);
|
|
AAOPF1_VEC_ENTRY (movaq, 0x5, ARGS_Q);
|
|
AAOPF1_VEC_ENTRY (movaqp, 0x7, ARGS_D);
|
|
|
|
E2K_OPCODE_VEC_ENTRY (fapb, parse_fapb_args);
|
|
E2K_OPCODE_VEC_ENTRY (movep, parse_movep_args);
|
|
|
|
if (mcpu >= 3)
|
|
E2K_OPCODE_VEC_ENTRY (flushts, parse_flushts_args);
|
|
|
|
E2K_OPCODE_VEC_ENTRY (clpandp, parse_cpl_args);
|
|
E2K_OPCODE_VEC_ENTRY (clplandp, parse_cpl_args);
|
|
|
|
E2K_OPCODE_VEC_ENTRY (set_mark, parse_set_mark_args);
|
|
E2K_OPCODE_VEC_ENTRY (vfdi, parse_vfdi_args);
|
|
}
|
|
|
|
static struct unpacked_instr {
|
|
unsigned int hs;
|
|
unsigned int ss;
|
|
unsigned int als[6];
|
|
unsigned int cs0;
|
|
unsigned short ales[6];
|
|
unsigned int cs1;
|
|
unsigned short aas[6];
|
|
unsigned short half_gap;
|
|
|
|
/* It should be impossible to have more than 16 words of GAP
|
|
in principle. */
|
|
unsigned int gap[16];
|
|
|
|
unsigned int lts[4];
|
|
unsigned int pls[3];
|
|
unsigned int cds[3];
|
|
|
|
unsigned char ss_present;
|
|
unsigned char als_present[6];
|
|
unsigned char cs0_present;
|
|
unsigned char ales_present[6];
|
|
unsigned char cs1_present;
|
|
unsigned char aas_present[6];
|
|
unsigned char half_gap_present;
|
|
unsigned char gap_present[16];
|
|
unsigned char lts_present[4];
|
|
unsigned char pls_present[3];
|
|
unsigned char cds_present[3];
|
|
|
|
unsigned int api_l[2];
|
|
unsigned int api_r[2];
|
|
} unpacked_instr;
|
|
|
|
|
|
static int
|
|
unpack_instr (bfd_byte *buf)
|
|
{
|
|
unsigned int i;
|
|
size_t pos = 0;
|
|
size_t gap;
|
|
int hsyll_cntr = 0;
|
|
unsigned int hs;
|
|
unsigned int mdl;
|
|
struct unpacked_instr *instr = &unpacked_instr;
|
|
memset (instr, 0, sizeof (unpacked_instr));
|
|
|
|
memcpy (&hs, &buf[pos], 4);
|
|
pos += 4;
|
|
instr->hs = hs;
|
|
|
|
/* Check for SS. */
|
|
if (hs & (0x1 << 12))
|
|
{
|
|
instr->ss_present = 1;
|
|
memcpy (&instr->ss, &buf[pos], 4);
|
|
pos += 4;
|
|
}
|
|
|
|
/* Check for available ALS syllables. */
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
if (hs & (1 << (26 + i)))
|
|
{
|
|
instr->als_present[i] = 1;
|
|
memcpy (&instr->als[i], &buf[pos], 4);
|
|
pos += 4;
|
|
}
|
|
}
|
|
|
|
/* Check for CS0. */
|
|
if (hs & (0x1 << 14))
|
|
{
|
|
instr->cs0_present = 1;
|
|
memcpy (&instr->cs0, &buf[pos], 4);
|
|
pos += 4;
|
|
}
|
|
|
|
/* If either `ALES5' or `ALES2' has been marked as present in HS, set its
|
|
value to default to properly account for the case when it's not allocated.
|
|
`ALES_PRESENT[{2,5}]' are treated this way in the code below: 0 means that
|
|
the syllable has been neither marked in HS, nor allocated; 1 - marked in
|
|
HS, but not allocated; 2 - not marked in HS, but allocated; 3 - both marked
|
|
in HS and allocated. */
|
|
if (hs & (0x1 << 25))
|
|
{
|
|
instr->ales_present[5] = 1;
|
|
instr->ales[5] = 0x01c0;
|
|
}
|
|
|
|
if (hs & (0x1 << 22))
|
|
{
|
|
instr->ales_present[2] = 1;
|
|
instr->ales[2] = 0x01c0;
|
|
}
|
|
|
|
|
|
/* Calculate the size of f1 fragment in bytes. For a valid instruction it
|
|
should be equal to either of `pos', `pos + 4' or `pos + 8'. What should I
|
|
do if it's not? */
|
|
mdl = ((hs & 0xf) + 1) * 4;
|
|
|
|
/* The following condition means that ALES{2,5} are physically present within
|
|
the wide instruction. However, they should be probably taken into account
|
|
only if HS.ale{2,5} are set. Should I disassemble them if these bits are
|
|
not set but the syllables physically exist? */
|
|
if (((hs & (0x1 << 15)) && mdl == pos + 8)
|
|
|| (!(hs & (0x1 << 15)) && mdl == pos + 4))
|
|
{
|
|
/* Fill in ALES5 and ALES2 syllables even if none of them is specified in
|
|
HS as present. This will let me output this syllable into disassembly
|
|
whichever case takes place. */
|
|
memcpy (&instr->ales[5], &buf[pos], 2);
|
|
memcpy (&instr->ales[2], &buf[pos + 2], 2);
|
|
|
|
/* Adjust `ALES_PRESENT[{5,2}]' as proposed above now that we know that
|
|
they are allocated. */
|
|
instr->ales_present[5] |= 0x2;
|
|
instr->ales_present[2] |= 0x2;
|
|
|
|
pos += 4;
|
|
}
|
|
|
|
/* Check for CS1. */
|
|
if (hs & (0x1 << 15))
|
|
{
|
|
instr->cs1_present = 1;
|
|
memcpy (&instr->cs1, &buf[pos], 4);
|
|
pos += 4;
|
|
}
|
|
|
|
/* A primitive control just for a moment. */
|
|
if (mdl != pos)
|
|
{
|
|
/* This is either an APB instruction or an invalid one. Let's stupidly
|
|
believe that the former takes place and signalize our caller about
|
|
that by returning 0. */
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Check for ALES{0,1,3,4}. */
|
|
for (i = 0; i < 5; i++)
|
|
{
|
|
if (i == 2)
|
|
continue;
|
|
|
|
if (hs & (0x1 << (20 + i)))
|
|
{
|
|
instr->ales_present[i] = 1;
|
|
|
|
/* Recall the idiotic order of half-syllables in the packed wide
|
|
instruction. */
|
|
memcpy (&instr->ales[i],
|
|
&buf[pos + 2 * ((hsyll_cntr & ~0x1) + 1
|
|
- (hsyll_cntr & 0x1))], 2);
|
|
hsyll_cntr++;
|
|
}
|
|
}
|
|
|
|
/* Check for AASj half-syllables. To encode them SS syllable of SF1 type
|
|
should be present. */
|
|
if (instr->ss_present && (instr->ss & (0x1 << 20)) == 0)
|
|
{
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (instr->ss & (0x1 << (12 + i)))
|
|
{
|
|
instr->aas_present[i >> 1] = 1;
|
|
instr->aas_present[2 + i] = 1;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
if (instr->aas_present[i])
|
|
{
|
|
/* Recall the idiotic order of half-syllables in the packed wide
|
|
instruction. Note that the first AAS half-syllable may share a
|
|
syllable with the last ALES. */
|
|
memcpy (&instr->aas[i],
|
|
&buf[pos + 2 * ((hsyll_cntr & ~0x1) + 1
|
|
- (hsyll_cntr & 0x1))], 2);
|
|
hsyll_cntr++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hsyll_cntr & 0x1)
|
|
{
|
|
/* Simplify the calculation of offset in BUF[] a bit by taking the above
|
|
condition into account. */
|
|
memcpy (&instr->half_gap, &buf[pos + 2 * (hsyll_cntr & ~0x1)], 2);
|
|
instr->half_gap_present = 1;
|
|
|
|
/* Ensure that hsyll_cntr is even. This is implied when calculating GAP
|
|
below. */
|
|
hsyll_cntr++;
|
|
}
|
|
|
|
/* Calculate the next 32-bit syllable's position. It may be the uppermost LTS
|
|
syllable. Note that I don't consider the case when LTS syllables reuse the
|
|
values encoded in the preceding ones, though according to `iset-v5.single'
|
|
this is quite legal. GAS doesn't produce such a code. Hopefully neither LAS
|
|
has ever done that . . . */
|
|
gap = pos + 2 * hsyll_cntr;
|
|
|
|
/* Set POS to point to the last syllable in the current wide instruction and
|
|
extract CDSj and PLSj syllables if any. */
|
|
pos = ((((hs & 0x70) >> 4) + 1) << 3) - 4;
|
|
|
|
/* Check for CDSj syllables. */
|
|
for (i = 0; i < ((hs & 0x30000) >> 16); i++)
|
|
{
|
|
instr->cds_present[i] = 1;
|
|
memcpy (&instr->cds[i], &buf[pos], 4);
|
|
pos -= 4;
|
|
}
|
|
|
|
|
|
/* Check for PLSj syllables. */
|
|
for (i = 0; i < ((hs & 0xc0000) >> 18); i++)
|
|
{
|
|
instr->pls_present[i] = 1;
|
|
memcpy (&instr->pls[i], &buf[pos], 4);
|
|
pos -= 4;
|
|
}
|
|
|
|
/* Now POS should point to the lowermost LTS0 syllable if any. If there are
|
|
no LTSj syllables in this instruction, POS should point to the last
|
|
syllable consisting of half-syllables.
|
|
|
|
If neither of these conditions holds true, believe that it's not a valid
|
|
synchronous instruction by analogy with the middle point test above.
|
|
Engineers are said to customize instructions with references to missing
|
|
literal syllables occasionally, but the lack of space for more substantial
|
|
syllables should not be allowed for. */
|
|
if (pos < gap && pos != gap - 4)
|
|
return 0;
|
|
|
|
/* Extract available LTSj syllables. */
|
|
for (i = 0; i < 4 && pos >= gap; i++)
|
|
{
|
|
instr->lts_present[i] = 1;
|
|
memcpy (&instr->lts[i], &buf[pos], 4);
|
|
pos -= 4;
|
|
}
|
|
|
|
/* It makes sense to enumerate GAP syllables in a normal order unlike LTS
|
|
ones. */
|
|
for (i = 0; i < 16 && gap <= pos; i++)
|
|
{
|
|
instr->gap_present[i] = 1;
|
|
memcpy (&instr->gap[i], &buf[gap], 4);
|
|
gap += 4;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* A convenience macro used in the functions below. */
|
|
#define my_printf(...) (*info->fprintf_func) (info->stream, __VA_ARGS__)
|
|
|
|
|
|
static int indentation;
|
|
|
|
static void
|
|
print_syllable (disassemble_info *info __attribute__ ((unused)),
|
|
const char *name __attribute__ ((unused)),
|
|
int half,
|
|
unsigned int val __attribute__ ((unused)))
|
|
{
|
|
/* Specifies that we are going to print an uppermost half-syllable. */
|
|
static int uppermost = 1;
|
|
|
|
#if defined ENABLE_E2K_ENCODINGS
|
|
int i;
|
|
char tpl[] = " ";
|
|
#endif /* ENABLE_E2K_ENCODINGS */
|
|
|
|
/* If a lowermost 16-bit half-syllable hasn't been printed out and we are
|
|
said to print a 32-bit syllable, this is a logical error. */
|
|
assert (uppermost == 1 || half == 1);
|
|
|
|
#if defined ENABLE_E2K_ENCODINGS
|
|
|
|
for (i = 0; i < 6 && name[i] != '\0'; i++)
|
|
tpl[i] = name[i];
|
|
|
|
if (half == 0)
|
|
my_printf ("%s%08x", tpl, val);
|
|
else
|
|
my_printf ("%s%s%04x", tpl, uppermost ? "" : " ", val);
|
|
|
|
#endif /* defined ENABLE_E2K_ENCODINGS */
|
|
|
|
#if defined ENABLE_E2K_ENCODINGS
|
|
if (half == 0 || uppermost == 0)
|
|
/* Two spaces should be appended after other syllables or lowermost
|
|
half-syllables. */
|
|
indentation = 2;
|
|
else
|
|
/* Six spaces should be appended after uppermost half-syllables. */
|
|
indentation = 1;
|
|
#else /* ! defined ENABLE_E2K_ENCODINGS */
|
|
/* Six spaces make no sense if encodings are disabled. */
|
|
indentation = 2;
|
|
#endif /* ! defined ENABLE_E2K_ENCODINGS */
|
|
|
|
/* Toggle UPPERMOST mark if a half syllable is printed out. */
|
|
if (half)
|
|
uppermost = 1 - uppermost;
|
|
}
|
|
|
|
#define print_syllable(...) print_syllable (info, __VA_ARGS__)
|
|
|
|
|
|
static void
|
|
end_syllable (disassemble_info *info)
|
|
{
|
|
#if ! defined ENABLE_E2K_ENCODINGS
|
|
/* In "release" mode `indentation == 2' means that nothing related to the
|
|
curent syllable has been printed out in fact. This check saves me the
|
|
trouble of putting all invocations of `print_syllable ()' under `defined
|
|
ENABLE_E2K_ENCODINGS' condition. */
|
|
if (indentation != 2)
|
|
#endif
|
|
my_printf ("\n");
|
|
}
|
|
|
|
#define end_syllable() end_syllable (info)
|
|
|
|
static void
|
|
indentate (disassemble_info *info)
|
|
{
|
|
/* Indentation prepended to all lines except the first one in multiline
|
|
instructions. */
|
|
const char *newline_indentation =
|
|
#if defined ENABLE_E2K_ENCODINGS
|
|
" "
|
|
#else /* ! defined ENABLE_E2K_ENCODINGS */
|
|
" "
|
|
#endif /* ! defined ENABLE_E2K_ENCODINGS */
|
|
;
|
|
|
|
if (indentation)
|
|
{
|
|
if (indentation == 1)
|
|
my_printf (" ");
|
|
else
|
|
my_printf (" ");
|
|
|
|
indentation = 0;
|
|
}
|
|
else
|
|
my_printf ("\n%s", newline_indentation);
|
|
}
|
|
|
|
#define indentate() indentate (info);
|
|
|
|
|
|
static const char *reg_prefix[] = {"", "d", "q", "qp"};
|
|
|
|
static void
|
|
print_src1 (disassemble_info *info, int chn, e2k_register_format fmt)
|
|
{
|
|
unsigned int src1 = (unpacked_instr.als[chn] & 0x00ff0000) >> 16;
|
|
|
|
assert (fmt == SINGLE || fmt == DOUBLE || fmt == QUAD || fmt == QPACKED);
|
|
|
|
if ((src1 & 0x80) == 0)
|
|
my_printf ("%%%sb[%d]", reg_prefix[fmt], src1 & 0x7f);
|
|
else if ((src1 & 0xc0) == 0x80)
|
|
my_printf ("%%%sr%d", reg_prefix[fmt], src1 & 0x3f);
|
|
else if ((src1 & 0xe0) == 0xc0)
|
|
my_printf ("0x%x", src1 & 0x1f);
|
|
else
|
|
my_printf ("%%%sg%d", reg_prefix[fmt], src1 & 0x1f);
|
|
}
|
|
|
|
static void
|
|
print_src2 (disassemble_info *info, int chn, e2k_register_format fmt)
|
|
{
|
|
const struct unpacked_instr *instr = &unpacked_instr;
|
|
unsigned int src2 = (instr->als[chn] & 0x0000ff00) >> 8;
|
|
|
|
assert (fmt == SINGLE || fmt == DOUBLE || fmt == QUAD || fmt == QPACKED);
|
|
|
|
if ((src2 & 0x80) == 0)
|
|
my_printf ("%%%sb[%d]", reg_prefix[fmt], src2 & 0x7f);
|
|
else if ((src2 & 0xc0) == 0x80)
|
|
my_printf ("%%%sr%d", reg_prefix[fmt], src2 & 0x3f);
|
|
else if ((src2 & 0xf0) == 0xc0)
|
|
my_printf ("0x%x", src2 & 0xf);
|
|
else if ((src2 & 0xf8) == 0xd0)
|
|
{
|
|
/* Print a 16-bit literal. */
|
|
unsigned int lit_num = src2 & 0x3;
|
|
int shift = (src2 & 0x4) ? 16 : 0;
|
|
|
|
if (lit_num > 1)
|
|
my_printf ("<illegal _f16s,_lts%d%s>", lit_num, shift ? "hi" : "lo");
|
|
else if (! instr->lts_present[lit_num])
|
|
my_printf ("<nonexistent _f16s,_lts%d%s>", lit_num,
|
|
shift ? "hi" : "lo");
|
|
else
|
|
{
|
|
#if defined ENABLE_E2K_ENCODINGS
|
|
my_printf ("_f16s,_lts%d%s ", lit_num, shift ? "hi" : "lo");
|
|
#else
|
|
my_printf ("_f16s ");
|
|
#endif /* ENABLE_E2K_ENCODINGS */
|
|
my_printf ("0x%x", ((instr->lts[lit_num] >> shift) & 0xffff));
|
|
}
|
|
}
|
|
else if ((src2 & 0xfc) == 0xd8)
|
|
{
|
|
/* Print a 32-bit literal. */
|
|
unsigned int lit_num = src2 & 0x3;
|
|
|
|
if (! instr->lts_present[lit_num])
|
|
my_printf ("<nonexistent _f32s,_lts%d>", lit_num);
|
|
else
|
|
{
|
|
#if defined ENABLE_E2K_ENCODINGS
|
|
my_printf ("_f32s,_lts%d ", lit_num);
|
|
#else
|
|
my_printf ("_f32s ");
|
|
#endif /* ENABLE_E2K_ENCODINGS */
|
|
my_printf ("0x%x", instr->lts[lit_num]);
|
|
}
|
|
}
|
|
else if ((src2 & 0xfc) == 0xdc)
|
|
{
|
|
/* Print a 64-bit literal. */
|
|
unsigned int lit_num = src2 & 0x3;
|
|
|
|
if (lit_num == 3)
|
|
my_printf ("<illegal _f64,_lts%d>", lit_num);
|
|
else if (! instr->lts_present[lit_num]
|
|
|| ! instr->lts_present[lit_num + 1])
|
|
my_printf ("<nonexistent _f64,_lts%d>", lit_num);
|
|
else
|
|
{
|
|
#if defined ENABLE_E2K_ENCODINGS
|
|
my_printf ("_f64,_lts%d ", lit_num);
|
|
#else
|
|
my_printf ("_f64 ");
|
|
#endif /* ENABLE_E2K_ENCODINGS */
|
|
|
|
if (instr->lts[lit_num + 1] == 0)
|
|
my_printf ("0x%x", instr->lts[lit_num]);
|
|
else
|
|
my_printf ("0x%x%08x", instr->lts[lit_num + 1], instr->lts[lit_num]);
|
|
}
|
|
}
|
|
else
|
|
my_printf ("%%%sg%d", reg_prefix[fmt], src2 & 0x1f);
|
|
}
|
|
|
|
static void
|
|
print_src3 (disassemble_info *info, int chn, int in_ales,
|
|
e2k_register_format fmt)
|
|
{
|
|
unsigned int src3 = (in_ales
|
|
? unpacked_instr.ales[chn] & 0xff
|
|
: unpacked_instr.als[chn] & 0x000000ff);
|
|
|
|
assert (fmt == SINGLE || fmt == DOUBLE || fmt == QUAD || fmt == QPACKED);
|
|
|
|
if ((src3 & 0x80) == 0)
|
|
my_printf ("%%%sb[%d]", reg_prefix[fmt], src3 & 0x7f);
|
|
else if ((src3 & 0xc0) == 0x80)
|
|
my_printf ("%%%sr%d", reg_prefix[fmt], src3 & 0x3f);
|
|
else if ((src3 & 0xe0) == 0xc0)
|
|
my_printf ("<illegal operand>");
|
|
else
|
|
my_printf ("%%%sg%d", reg_prefix[fmt], src3 & 0x1f);
|
|
}
|
|
|
|
static void
|
|
print_dst (disassemble_info *info, unsigned int dst, e2k_register_format fmt)
|
|
{
|
|
assert (fmt == SINGLE || fmt == DOUBLE || fmt == QUAD || fmt == QPACKED);
|
|
|
|
if ((dst & 0x80) == 0)
|
|
my_printf ("%%%sb[%d]", reg_prefix[fmt], dst & 0x7f);
|
|
else if ((dst & 0xc0) == 0x80)
|
|
my_printf ("%%%sr%d", reg_prefix[fmt], dst & 0x3f);
|
|
else if ((dst & 0xe0) == 0xc0)
|
|
{
|
|
/* FIXME: these names are to be shared with GAS somehow. */
|
|
static const char *special[] = {
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
"%tst",
|
|
"%tc",
|
|
"%tcd",
|
|
NULL,
|
|
"%ctpr1",
|
|
"%ctpr2",
|
|
"%ctpr3",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
"%empty.lo",
|
|
"%empty.hi"
|
|
};
|
|
|
|
unsigned int idx = dst & 0x1f;
|
|
my_printf ("%s", (special[idx]
|
|
? special[idx]
|
|
: "<reserved special register>"));
|
|
}
|
|
else
|
|
my_printf ("%%%sg%d", reg_prefix[fmt], dst & 0x1f);
|
|
}
|
|
|
|
static void
|
|
print_dst_in_als (disassemble_info *info, int chn, e2k_register_format fmt)
|
|
{
|
|
print_dst (info, unpacked_instr.als[chn] & 0x000000ff, fmt);
|
|
}
|
|
|
|
static void
|
|
print_dst2 (disassemble_info *info, int chn)
|
|
{
|
|
unsigned int pred_num = unpacked_instr.als[chn] & 0x1f;
|
|
my_printf ("%%pred%d", pred_num);
|
|
}
|
|
|
|
static void
|
|
print_state_reg (disassemble_info *info, int code)
|
|
{
|
|
static const char *names[] = {
|
|
/* ALT_ENTRies are ignored for the sake of unambiguous disassembly. */
|
|
#define ALT_ENTRY(a,b,c,d)
|
|
#define ENTRY(a,b,c,d) [b] = a,
|
|
#define __e3s(arg) arg
|
|
#define __e2s(arg) arg
|
|
#define __v5(arg) arg
|
|
|
|
/* ALT_ENTRies below make sense for assembler only to allow for different
|
|
variants of state register names. They are ignored in disassembler. */
|
|
|
|
ENTRY("%psr", 0x00, 1, 0)
|
|
ENTRY("%wd", 0x01, 0, 0)
|
|
ENTRY("%core_mode", 0x04, 0, 0)
|
|
ENTRY("%cwd", 0x06, 1, 0)
|
|
ENTRY("%psp.hi", 0x07, 1, 0)
|
|
ENTRY("%psp.lo", 0x09, 1, 0)
|
|
ENTRY("%pshtp", 0x0b, 1, 0)
|
|
ENTRY("%pcsp.hi", 0x0d, 1, 0)
|
|
ENTRY("%pcsp.lo", 0x0f, 1, 0)
|
|
ENTRY("%pcshtp", 0x13, 0, 0)
|
|
ENTRY("%ctpr1", 0x15, 0, 0)
|
|
ENTRY("%ctpr2", 0x16, 0, 0)
|
|
ENTRY("%ctpr3", 0x17, 0, 0)
|
|
ENTRY("%sbr", 0x1e, 0, 0)
|
|
/* This flavour of %sbr register name won't be used in disassembler. */
|
|
ALT_ENTRY("%usbr", 0x1e, 0, 0)
|
|
ENTRY("%cutd", 0x21, 1, 0)
|
|
ENTRY("%eir", 0x23, 0, 0)
|
|
|
|
/* Note that `%tsd' register corresponding to 0x24 has been "officially"
|
|
eliminated from all versions of Elbrus instruction set (see Bug #74420
|
|
and Bug #53587). In fact it's not supported starting from elbrus-v3
|
|
only. */
|
|
|
|
ENTRY("%cuir", 0x25, 0, 0)
|
|
|
|
ENTRY("%oscud.hi", 0x26, 1, 0)
|
|
ENTRY("%oscud.lo", 0x27, 1, 0)
|
|
ENTRY("%osgd.hi", 0x28, 1, 0)
|
|
ENTRY("%osgd.lo", 0x29, 1, 0)
|
|
ENTRY("%osem", 0x2a, 1, 0)
|
|
ENTRY("%usd.hi", 0x2c, 1, 0)
|
|
ENTRY("%usd.lo", 0x2d, 1, 0)
|
|
|
|
/* `%tr' register which used to be encoded with 0x2e has been eliminated
|
|
together with `%tsd' (see above). */
|
|
|
|
ENTRY("%osr0", 0x2f, 0, 0)
|
|
ENTRY("%cud.hi", 0x30, 0, 1)
|
|
ENTRY("%cud.lo", 0x31, 0, 1)
|
|
ENTRY("%gd.hi", 0x32, 0, 1)
|
|
ENTRY("%gd.lo", 0x33, 0, 1)
|
|
ENTRY("%cs.hi", 0x34, 1, 0)
|
|
ENTRY("%cs.lo", 0x35, 1, 0)
|
|
ENTRY("%ds.hi", 0x36, 1, 0)
|
|
ENTRY("%ds.lo", 0x37, 1, 0)
|
|
ENTRY("%es.hi", 0x38, 1, 0)
|
|
ENTRY("%es.lo", 0x39, 1, 0)
|
|
ENTRY("%fs.hi", 0x3a, 1, 0)
|
|
ENTRY("%fs.lo", 0x3b, 1, 0)
|
|
ENTRY("%gs.hi", 0x3c, 1, 0)
|
|
ENTRY("%gs.lo", 0x3d, 1, 0)
|
|
ENTRY("%ss.hi", 0x3e, 1, 0)
|
|
ENTRY("%ss.lo", 0x3f, 1, 0)
|
|
ENTRY("%dibcr", 0x40, 0, 0)
|
|
ENTRY("%dimcr", 0x41, 0, 0)
|
|
ENTRY("%dibsr", 0x42, 0, 0)
|
|
ENTRY("%dtcr", 0x43, 0, 0)
|
|
ENTRY("%dibar0", 0x48, 0, 0)
|
|
ENTRY("%dibar1", 0x49, 0, 0)
|
|
ENTRY("%dibar2", 0x4a, 0, 0)
|
|
ENTRY("%dibar3", 0x4b, 0, 0)
|
|
ENTRY("%dimar0", 0x4c, 0, 0)
|
|
ENTRY("%dimar1", 0x4d, 0, 0)
|
|
ENTRY("%dtarf", 0x4e, 0, 0)
|
|
ENTRY("%dtart", 0x4f, 0, 0)
|
|
ENTRY("%cr0.hi", 0x51, 0, 0)
|
|
ENTRY("%cr0.lo", 0x53, 0, 0)
|
|
ENTRY("%cr1.hi", 0x55, 0, 0)
|
|
ENTRY("%cr1.lo", 0x57, 0, 0)
|
|
ENTRY(__e2s("%sclkm1"), 0x70, 0, 0)
|
|
ENTRY(__e2s("%sclkm2"), 0x71, 0, 0)
|
|
ENTRY(__e3s("%cu_hw0"), 0x78, 0, 0)
|
|
ENTRY(__v5("%cu_hw1"), 0x79, 0, 0)
|
|
ENTRY("%upsr", 0x80, 0, 0)
|
|
ENTRY("%ip", 0x81, 0, 0)
|
|
ENTRY("%nip", 0x82, 0, 0)
|
|
ENTRY("%lsr1", 0xc3, 0, 0)
|
|
ENTRY("%lsr", 0x83, 0, 0)
|
|
ENTRY("%pfpfr", 0x84, 0, 0)
|
|
ENTRY("%fpcr", 0x85, 0, 0)
|
|
ENTRY("%fpsr", 0x86, 0, 0)
|
|
ENTRY("%ilcr1", 0xc7, 0, 0)
|
|
ENTRY("%ilcr", 0x87, 0, 0)
|
|
ENTRY("%br", 0x88, 0, 0)
|
|
ENTRY("%bgr", 0x89, 0, 0)
|
|
ENTRY(__e3s("%idr"), 0x8a, 0, 0)
|
|
ENTRY("%clkr", 0x90, 0, 0)
|
|
ENTRY("%rndpr", 0x91, 0, 0)
|
|
ENTRY(__e2s("%sclkr"), 0x92, 0, 0)
|
|
ENTRY("%tir.hi", 0x9c, 0, 0)
|
|
ENTRY("%tir.lo", 0x9d, 0, 0)
|
|
ENTRY("%rpr.lo", 0xa0, 0, 0)
|
|
ENTRY("%sbbp", 0xa1, 0, 0)
|
|
ENTRY("%rpr.hi", 0xa2, 0, 0)
|
|
/* This flavour of %rpr.hi register name won't be used in disassembler. */
|
|
ALT_ENTRY("%rpr", 0xa0, 0, 0)
|
|
ENTRY("%upsrm", 0xc0, 0, 0)
|
|
|
|
#undef __v5
|
|
#undef __e2s
|
|
#undef __e3s
|
|
#undef ENTRY
|
|
#undef ALT_ENTRY
|
|
};
|
|
|
|
my_printf ("%s", names[code]);
|
|
}
|
|
|
|
|
|
static void
|
|
print_predicates (disassemble_info *info,
|
|
int chn,
|
|
int mrgc)
|
|
{
|
|
int i;
|
|
/* Has the first separator been printed out? For RLP predicates this is a
|
|
question sign, for MRGC - a comma. */
|
|
int first_separator = 0;
|
|
const struct unpacked_instr *instr = &unpacked_instr;
|
|
/* Required RLP for this ALC. */
|
|
unsigned short req_rlp = chn <= 2 ? 0 : 1;
|
|
/* Required MRGC for this ALC in case ALF proves to be `merge{s,d}'. */
|
|
unsigned short req_mrgc = chn <= 2 ? 2 : 3;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
int j;
|
|
unsigned short *cds;
|
|
|
|
if (! instr->cds_present[i])
|
|
continue;
|
|
|
|
cds = (unsigned short *) &instr->cds[i];
|
|
for (j = 0; j < 2; j++)
|
|
{
|
|
unsigned short opc = (cds[j] & 0xc000) >> 14;
|
|
unsigned short mask = (cds[j] & 0x3c00) >> 10;
|
|
/* Is this RLP suitable for CHN and is CHN present in its mask? */
|
|
if (((! mrgc && opc == req_rlp)
|
|
|| (mrgc && opc == req_mrgc))
|
|
&& (mask & (1 << (chn - 3 * req_rlp))))
|
|
{
|
|
unsigned short neg = (cds[j] & 0x0380) >> 7;
|
|
unsigned short pred = cds[j] & 0x7f;
|
|
unsigned short num = pred & 0x1f;
|
|
|
|
if (! first_separator)
|
|
{
|
|
my_printf (mrgc ? ", " : " ? ");
|
|
first_separator = 1;
|
|
}
|
|
else
|
|
/* LDIS separates several RLP predicates acting on the same ALC
|
|
with a comma. Note that several MRGC predicates related to
|
|
the same ALC are inappropriate in fact . . . */
|
|
my_printf (mrgc ? " " : ", ");
|
|
|
|
if (neg & (1 << (chn - 3 * req_rlp)))
|
|
my_printf ("~");
|
|
|
|
if ((pred & 0x60) == 0x40)
|
|
my_printf ("%%pcnt%hd", num);
|
|
else if ((pred & 0x60) == 0x60)
|
|
my_printf ("%%pred%hd", num);
|
|
else
|
|
my_printf ("<invalid predicate>");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_alf (disassemble_info *info, int chn)
|
|
{
|
|
e2k_opcode_hash *cur;
|
|
const e2k_alf_opcode_templ *match = NULL;
|
|
const char *name;
|
|
char syll_name[5];
|
|
const struct unpacked_instr *instr = &unpacked_instr;
|
|
unsigned int opc = (instr->als[chn] & 0x7f000000) >> 24;
|
|
unsigned int sm = (instr->als[chn] & 0x80000000) >> 31;
|
|
|
|
snprintf (syll_name, 5, "ALS%d", chn);
|
|
print_syllable (syll_name, 0, instr->als[chn]);
|
|
|
|
for (cur = opcode_hash_table[opc]; cur != NULL; cur = cur->next)
|
|
{
|
|
const e2k_alf_opcode_templ *templ = cur->templ;
|
|
|
|
if (! templ->allowed_channels[chn])
|
|
continue;
|
|
|
|
if (templ->alopf == ALOPF1
|
|
/* `merge{s,d} are considered to be ALOPF1 according to
|
|
iset-vX.single and thus have a short encoding. */
|
|
|| templ->alopf == MERGE
|
|
|| templ->alopf == ALOPF2
|
|
|| templ->alopf == ALOPF3
|
|
|| templ->alopf == ALOPF7
|
|
|| templ->alopf == ALOPF8)
|
|
{
|
|
/* If the current instruction has one of the short encodings the
|
|
related ALES should not be present. (By "present" I mean that it
|
|
has been marked in HS, not allocated in a wide instruction. This
|
|
makes difference for `ALES{5,2}'.) */
|
|
if (instr->ales_present[chn] & 0x1)
|
|
continue;
|
|
|
|
if (templ->alopf == ALOPF2)
|
|
{
|
|
const e2k_alf2_opcode_templ *alf2;
|
|
alf2 = (const e2k_alf2_opcode_templ *) templ;
|
|
|
|
if (((instr->als[chn] & 0x00ff0000) >> 16) != alf2->opce)
|
|
continue;
|
|
}
|
|
else if (templ->alopf == ALOPF7)
|
|
{
|
|
const e2k_alf7_opcode_templ *alf7;
|
|
alf7 = (const e2k_alf7_opcode_templ *) templ;
|
|
|
|
if (((instr->als[chn] & 0x000000e0) >> 5) != alf7->cmpopce)
|
|
continue;
|
|
}
|
|
else if (templ->alopf == ALOPF8)
|
|
{
|
|
const e2k_alf8_opcode_templ *alf8;
|
|
alf8 = (const e2k_alf8_opcode_templ *) templ;
|
|
|
|
if (((instr->als[chn] & 0x000000e0) >> 5) != alf8->cmpopce)
|
|
continue;
|
|
}
|
|
}
|
|
else if (! (instr->ales_present[chn] & 0x1))
|
|
/* ALES should be present for instructions having a long encoding. */
|
|
continue;
|
|
else
|
|
{
|
|
if (templ->alopf == ALOPF10
|
|
/* According to `iset-vX.single' these ones are ALOPF10. */
|
|
/* || templ->alopf == AAURW */)
|
|
{
|
|
if (strcmp (templ->name, "staaqp") == 0)
|
|
{
|
|
if (instr->ales[chn] != 0x02c0)
|
|
continue;
|
|
}
|
|
else if (instr->ales[chn] != 0x01c0)
|
|
continue;
|
|
}
|
|
else if (templ->alopf == ALOPF11
|
|
|| templ->alopf == ALOPF11_MERGE)
|
|
{
|
|
const e2k_alopf11_opcode_templ *alopf11;
|
|
|
|
alopf11 = (const e2k_alopf11_opcode_templ *) templ;
|
|
if (instr->ales[chn] != ((alopf11->ales_opc2[chn] << 8)
|
|
| alopf11->ales_opce))
|
|
continue;
|
|
}
|
|
else if (templ->alopf == ALOPF11_LIT8)
|
|
{
|
|
const e2k_alopf11_lit8_opcode_templ *alopf11_lit8;
|
|
|
|
alopf11_lit8 = (const e2k_alopf11_lit8_opcode_templ *) templ;
|
|
if ((instr->ales[chn] & 0xff00)
|
|
!= ((alopf11_lit8->ales_opc2[chn] << 8)))
|
|
continue;
|
|
}
|
|
|
|
else if (templ->alopf == ALOPF12)
|
|
{
|
|
const e2k_alopf12_opcode_templ *alopf12;
|
|
|
|
alopf12 = (const e2k_alopf12_opcode_templ *) templ;
|
|
if ((instr->als[chn] & 0x00ff0000) != (unsigned) (alopf12->opce << 16)
|
|
|| instr->ales[chn] != ((alopf12->ales_opc2 << 8)
|
|
| alopf12->ales_opce))
|
|
continue;
|
|
}
|
|
else if (templ->alopf == ALOPF12_PSHUFH)
|
|
{
|
|
/* Note that for PSHUFH ALES.opce holds the COUNT parameter,
|
|
therefore don't check for it. */
|
|
const e2k_alopf12_opcode_templ *alopf12;
|
|
alopf12 = (const e2k_alopf12_opcode_templ *) templ;
|
|
if ((instr->als[chn] & 0x00ff0000) != (unsigned) (alopf12->opce << 16)
|
|
|| (instr->ales[chn] & 0xff00) != (alopf12->ales_opc2 << 8))
|
|
continue;
|
|
}
|
|
else if (templ->alopf == ALOPF13)
|
|
{
|
|
const e2k_alopf13_opcode_templ *alopf13;
|
|
|
|
alopf13 = (const e2k_alopf13_opcode_templ *) templ;
|
|
if (instr->ales[chn] != ((alopf13->ales_opc2 << 8)
|
|
| alopf13->ales_opce))
|
|
continue;
|
|
}
|
|
else if (templ->alopf == ALOPF15
|
|
|| templ->alopf == ALOPF16)
|
|
{
|
|
if (instr->ales[chn] != 0x01c0)
|
|
continue;
|
|
}
|
|
/* Note that according to `iset-vX.single' these ones are ALOPF19. */
|
|
else if (templ->alopf == AAURR)
|
|
{
|
|
if (instr->ales[chn] != 0x01c0)
|
|
continue;
|
|
}
|
|
else if (templ->alopf == ALOPF21
|
|
|| templ->alopf == ALOPF21_MERGE)
|
|
{
|
|
const e2k_alopf21_opcode_templ *alopf21;
|
|
|
|
alopf21 = (const e2k_alopf21_opcode_templ *) templ;
|
|
if ((instr->ales[chn] & 0xff00) != (alopf21->ales_opc2 << 8))
|
|
continue;
|
|
}
|
|
else if (templ->alopf == ALOPF22)
|
|
{
|
|
/* FIXME: according to the up-to-date version of iset-v5.single
|
|
there are no instructions with ALOPF22 encoding at all! Where
|
|
have I taken them from then? Moreover, according to the
|
|
underlying test, their ALES seems to be of ALEF2 format, not
|
|
ALEF1. What's the difference between them and ALOPF12
|
|
instructions then? */
|
|
const e2k_alf2_opcode_templ *alf2;
|
|
alf2 = (const e2k_alf2_opcode_templ *) templ;
|
|
|
|
if (instr->ales[chn] != 0x01c0
|
|
|| ((instr->als[chn] & 0x00ff0000) >> 16) != alf2->opce)
|
|
continue;
|
|
}
|
|
else if (templ->alopf == ALOPF17)
|
|
{
|
|
/* FIXME: eventually migrate to `e2k_alopf17_opcode_templ' here
|
|
simultaneously with other components. */
|
|
const e2k_alf7_opcode_templ *alf7;
|
|
alf7 = (const e2k_alf7_opcode_templ *) templ;
|
|
|
|
if (instr->ales[chn] != (0x0200 | alf7->cmpopce))
|
|
continue;
|
|
}
|
|
/* Ignore them for now since they are a special case of `STAA*'
|
|
instructions which are disassembled as ALOPF10 above. */
|
|
else if (templ->alopf == AAURW)
|
|
continue;
|
|
/* It's a 100% internal error if we turned out to be incapable of
|
|
recognizing our own instruction template. It would be easier to
|
|
skip it silently, of course, and not to disappoint the user, who
|
|
would obtain an unremarkable "unrecognized" instruction in the
|
|
worst case. But . . . */
|
|
else
|
|
assert (0);
|
|
}
|
|
|
|
if (match == NULL)
|
|
/* This is the first matching ALF template. */
|
|
match = templ;
|
|
else
|
|
/* More than one matching ALF templates found. */
|
|
break;
|
|
}
|
|
|
|
if (match == NULL)
|
|
name = "unrecognized";
|
|
else if (cur == NULL)
|
|
/* Exactly one matching template found. */
|
|
name = match->name;
|
|
else
|
|
{
|
|
/* More than one matching templates found. */
|
|
name = "ambiguous";
|
|
match = NULL;
|
|
}
|
|
|
|
/* Stupidly prevent instructions requiring a pair of ALCes from being
|
|
disassembled twice.
|
|
FIXME: get rid of this code along with the corresponding one in GAS as
|
|
soon as you add info on "real_alses[]" to `opcodes/e2k-opc.h'. */
|
|
if (((chn == 1 || chn == 4)
|
|
&& (strcmp (name, "movtq") == 0
|
|
|| strcmp (name, "gdtoap") == 0
|
|
|| strcmp (name, "cast") == 0
|
|
|| strcmp (name, "odtoap") == 0))
|
|
|| ((chn == 2 || chn == 5)
|
|
&& (strcmp (name, "ldgdq") == 0
|
|
|| strcmp (name, "ldcudq") == 0
|
|
|| strcmp (name, "ldapq") == 0
|
|
|| strcmp (name, "ldq") == 0
|
|
|
|
|| strcmp (name, "ldcsq") == 0
|
|
|| strcmp (name, "lddsq") == 0
|
|
|| strcmp (name, "ldesq") == 0
|
|
|| strcmp (name, "ldfsq") == 0
|
|
|| strcmp (name, "ldgsq") == 0
|
|
|| strcmp (name, "ldssq") == 0))
|
|
|| (chn == 5
|
|
&& (strcmp (name, "staaq") == 0
|
|
|| strcmp (name, "stgdq") == 0
|
|
|| strcmp (name, "stapq") == 0
|
|
|| strcmp (name, "aaurwq") == 0
|
|
|| strcmp (name, "aaurrq") == 0
|
|
|
|
|| strcmp (name, "stcsq") == 0
|
|
|| strcmp (name, "stdsq") == 0
|
|
|| strcmp (name, "stesq") == 0
|
|
|| strcmp (name, "stfsq") == 0
|
|
|| strcmp (name, "stgsq") == 0
|
|
|| strcmp (name, "stssq") == 0))
|
|
|| (chn == 1
|
|
&& strcmp (name, "getsap") == 0)
|
|
|| ((chn == 1 || chn == 4)
|
|
&& (strcmp (name, "aptoap") == 0
|
|
|| strcmp (name, "aptoapb") == 0)))
|
|
{
|
|
end_syllable ();
|
|
return;
|
|
}
|
|
|
|
indentate ();
|
|
my_printf ("%s,%d%s ", name, chn, sm ? ",sm" : "");
|
|
|
|
if (match == NULL)
|
|
{
|
|
end_syllable ();
|
|
return;
|
|
}
|
|
|
|
/* Now print the arguments. */
|
|
if (match->alopf == ALOPF1
|
|
/* In iset-vX.single `merge{s,d}' are said to be ALOPF1. */
|
|
|| match->alopf == MERGE
|
|
|| match->alopf == ALOPF11
|
|
|| match->alopf == ALOPF11_LIT8
|
|
|| match->alopf == ALOPF11_MERGE
|
|
|| match->alopf == ALOPF21
|
|
|| match->alopf == ALOPF21_MERGE)
|
|
{
|
|
/* FIXME: here I make use of the fact that in `e2k_alopf{1,2}1_opcode_
|
|
templ' `arg_fmt[]' is located at the same offset as in
|
|
`e2k_alf1_opcode_templ'. */
|
|
const e2k_alf1_opcode_templ *alf1 = (const e2k_alf1_opcode_templ *) match;
|
|
|
|
print_src1 (info, chn, alf1->arg_fmt[0]);
|
|
my_printf (", ");
|
|
print_src2 (info, chn, alf1->arg_fmt[1]);
|
|
my_printf (", ");
|
|
|
|
if (match->alopf == ALOPF11_LIT8)
|
|
/* Output an additional lit8 argument encoded in `ALES.opce'. */
|
|
my_printf ("0x%x, ", (unsigned) (instr->ales[chn] & 0xff));
|
|
else if (match->alopf == ALOPF21 || match->alopf == ALOPF21_MERGE)
|
|
{
|
|
print_src3 (info, chn, 1, alf1->arg_fmt[2]);
|
|
my_printf (", ");
|
|
}
|
|
|
|
print_dst_in_als (info, chn,
|
|
alf1->arg_fmt[match->alopf == ALOPF21 ? 3 : 2]);
|
|
}
|
|
else if (match->alopf == ALOPF2
|
|
|| match->alopf == ALOPF12
|
|
|| match->alopf == ALOPF12_PSHUFH
|
|
|| match->alopf == ALOPF22)
|
|
{
|
|
const e2k_alf2_opcode_templ *alf2 = (const e2k_alf2_opcode_templ *) match;
|
|
|
|
print_src2 (info, chn, alf2->arg_fmt[0]);
|
|
my_printf (", ");
|
|
|
|
/* pshufh accepts an additional lit8 argument encoded in `ALES.opce'. */
|
|
if (match->alopf == ALOPF12_PSHUFH)
|
|
my_printf ("0x%x, ", (unsigned) (instr->ales[chn] & 0xff));
|
|
|
|
print_dst_in_als (info, chn, alf2->arg_fmt[1]);
|
|
}
|
|
else if (match->alopf == ALOPF3
|
|
|| match->alopf == ALOPF13)
|
|
{
|
|
const e2k_alf3_opcode_templ *alf3 = (const e2k_alf3_opcode_templ *) match;
|
|
|
|
print_src1 (info, chn, alf3->arg_fmt[0]);
|
|
my_printf (", ");
|
|
print_src2 (info, chn, alf3->arg_fmt[1]);
|
|
my_printf (", ");
|
|
print_src3 (info, chn, 0, alf3->arg_fmt[2]);
|
|
}
|
|
else if (match->alopf == ALOPF15)
|
|
{
|
|
const e2k_alopf15_opcode_templ *alopf15
|
|
= (const e2k_alopf15_opcode_templ *) match;
|
|
|
|
print_src2 (info, chn, alopf15->arg_fmt);
|
|
my_printf (", ");
|
|
print_state_reg (info, instr->als[chn] & 0xff);
|
|
}
|
|
else if (match->alopf == ALOPF16)
|
|
{
|
|
const e2k_alopf16_opcode_templ *alopf16
|
|
= (const e2k_alopf16_opcode_templ *) match;
|
|
|
|
print_state_reg (info, (instr->als[chn] & 0x00ff0000) >> 16);
|
|
my_printf (", ");
|
|
print_dst_in_als (info, chn, alopf16->arg_fmt);
|
|
}
|
|
else if (match->alopf == ALOPF7 || match->alopf == ALOPF17)
|
|
{
|
|
const e2k_alf7_opcode_templ *alf7
|
|
= (const e2k_alf7_opcode_templ *) match;
|
|
|
|
print_src1 (info, chn, alf7->arg_fmt[0]);
|
|
my_printf (", ");
|
|
print_src2 (info, chn, alf7->arg_fmt[1]);
|
|
my_printf (", ");
|
|
print_dst2 (info, chn);
|
|
}
|
|
else if (match->alopf == ALOPF8)
|
|
{
|
|
const e2k_alf8_opcode_templ *alf8
|
|
= (const e2k_alf8_opcode_templ *) match;
|
|
|
|
print_src2 (info, chn, alf8->arg_fmt);
|
|
my_printf (", ");
|
|
print_dst2 (info, chn);
|
|
}
|
|
#if 0
|
|
else if (match->alopf == ALOPF19)
|
|
{
|
|
}
|
|
#endif /* 0 */
|
|
else if (match->alopf == ALOPF10)
|
|
{
|
|
unsigned int opce1 = (instr->als[chn] & 0x00ffff00) >> 8;
|
|
unsigned int lt = opce1 & 0x3;
|
|
unsigned int am = (opce1 & 0x4) >> 2;
|
|
unsigned int incr = (opce1 & 0x70) >> 4;
|
|
unsigned int ind = (opce1 & 0x780) >> 7;
|
|
unsigned int d = (opce1 & 0xf800) >> 11;
|
|
|
|
const e2k_alf10_opcode_templ *alf10
|
|
= (const e2k_alf10_opcode_templ *) match;
|
|
|
|
|
|
print_src3 (info, chn, 0, alf10->arg_fmt);
|
|
my_printf (", %%aad%d [ %%aasti%d", d, ind);
|
|
|
|
if (lt)
|
|
{
|
|
if ( instr->lts_present[lt - 1])
|
|
my_printf (" + _f32s,_lts%d 0x%x", lt - 1, instr->lts[lt - 1]);
|
|
else
|
|
my_printf (" + <non-existent literal>");
|
|
}
|
|
|
|
my_printf (" ]");
|
|
|
|
if (am)
|
|
{
|
|
indentate ();
|
|
my_printf ("incr,%d %%aaincr%d", chn, incr);
|
|
}
|
|
|
|
}
|
|
|
|
if (match->need_mas
|
|
/* I wonder if instructions requiring MAS may be placed into MASless ALC
|
|
in principle . . . */
|
|
&& (chn == 0 || chn == 2 || chn == 3 || chn == 5)
|
|
&& instr->cs1_present
|
|
&& ((instr->cs1 & 0xf0000000) >> 28) == 6)
|
|
{
|
|
static int shift[] = {21, -1, 14, 7, -1, 0};
|
|
unsigned int mas = (instr->cs1 & (0x7f << shift[chn])) >> shift[chn];
|
|
|
|
/* There's no point in printing zero MAS. */
|
|
if (mas)
|
|
my_printf (", mas = 0x%x", mas);
|
|
}
|
|
|
|
/* Print MRGC predicates if they make sense. */
|
|
if (match->alopf == MERGE
|
|
|| match->alopf == ALOPF11_MERGE
|
|
|| match->alopf == ALOPF21_MERGE)
|
|
print_predicates (info, chn, 1);
|
|
|
|
/* Print RLP predicates. */
|
|
print_predicates (info, chn, 0);
|
|
end_syllable ();
|
|
}
|
|
|
|
static void
|
|
print_elp (disassemble_info *info, int idx, unsigned int elp,
|
|
int explicitly_needed)
|
|
{
|
|
/* One may wish to print ELP out either if it takes part in the formation of
|
|
an output %predX (explicitly needed) or if has some valuable side effect.
|
|
The latter is likely to take place in case of `pass %rndpredY, @pZ' only
|
|
(see Bug #101471). */
|
|
if (! explicitly_needed
|
|
&& ! ((elp & 0x60) == 0x40
|
|
&& ((elp & 0x1f) >= 1 && (elp & 0x1f) <= 15)))
|
|
return;
|
|
|
|
/* FIXME: `indentate ()' can't be called from the outside of this function
|
|
at present since it's not evident if the latter will output anything at
|
|
all and thus indentation will be needed. */
|
|
indentate ();
|
|
|
|
my_printf ("pass ");
|
|
|
|
if ((elp & 0x40) == 0)
|
|
{
|
|
if ((elp & 0x3f) == 0)
|
|
my_printf ("%%lcntex");
|
|
else
|
|
{
|
|
int i;
|
|
my_printf ("%%spred");
|
|
for (i = 0; i < 5; i++)
|
|
{
|
|
if (elp & (1 << i))
|
|
my_printf ("%d", i);
|
|
}
|
|
}
|
|
}
|
|
else if ((elp & 0x60) == 0x40)
|
|
{
|
|
if ((elp & 0x1f) == 0)
|
|
my_printf ("%%bgrpred");
|
|
else if ((elp & 0x1f) <= 15)
|
|
my_printf ("%%rndpred%d", elp & 0x1f);
|
|
else
|
|
my_printf ("<invalid pred>");
|
|
}
|
|
else
|
|
my_printf ("%%pred%d", elp & 0x1f);
|
|
|
|
my_printf (", @p%d", idx);
|
|
}
|
|
|
|
static void
|
|
print_clp (disassemble_info *info, int idx, unsigned int clp)
|
|
{
|
|
static const char *names[4] = {"andp", "landp", "reserved", "movep"};
|
|
unsigned int opc = (clp & 0xc000) >> 14;
|
|
unsigned int neg0 = (clp & 0x2000) >> 13;
|
|
unsigned int p0 = (clp & 0x1c00) >> 10;
|
|
unsigned int neg1 = (clp & 0x0200) >> 9;
|
|
unsigned int p1 = (clp & 0x01c0) >> 6;
|
|
unsigned int vdst = (clp & 0x0020) >> 5;
|
|
unsigned int pdst = (clp &0x001f);
|
|
|
|
my_printf ("%s %s@p%d, %s@p%d, @p%d", names[opc], neg0 ? "~" : "", p0,
|
|
neg1 ? "~" : "", p1, idx);
|
|
|
|
if (vdst)
|
|
{
|
|
indentate ();
|
|
my_printf ("pass @p%d, %%pred%d", idx, pdst);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
print_pls_syllables (disassemble_info *info)
|
|
{
|
|
int i;
|
|
/* The first pass is always needed. */
|
|
int once_more = 1;
|
|
const struct unpacked_instr *instr = &unpacked_instr;
|
|
int need[7] = {0, 0, 0, 0, 0, 0, 0};
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
/* Check if PLS{i}.{C,M}LP.vdst is set to 1 and mark the corresponding
|
|
pdst local predicates as needed. Note that for correctly encoded MLP
|
|
operations it should be set to one unconditionally. Should I allow for
|
|
incorrectly encoded MLPs? */
|
|
if (instr->pls_present[i] && (instr->pls[i] & 0x20))
|
|
need[4 + i] = 1;
|
|
}
|
|
|
|
while (once_more)
|
|
{
|
|
/* It will be set to 1 if some of `@p{i >= 4}' dependencies are
|
|
found. */
|
|
once_more = 0;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
if (need[4 + i] == 1)
|
|
{
|
|
int p0;
|
|
int p1;
|
|
|
|
p0 = (instr->pls[i] & 0x1c00) >> 10;
|
|
p1 = (instr->pls[i] & 0x1c0) >> 6;
|
|
|
|
/* Note that only `@p0, . . ., @p6' are allowed, while `@p7' can
|
|
be formally encoded as well. */
|
|
if (p0 < 7 && need[p0] == 0)
|
|
{
|
|
need[p0] = 1;
|
|
|
|
/* One more pass will be needed to analyze dependencies of
|
|
this predicate. */
|
|
if (p0 >= 4)
|
|
once_more = 1;
|
|
}
|
|
|
|
if (p1 < 7 && need[p1] == 0)
|
|
{
|
|
need[p1] = 1;
|
|
|
|
if (p1 >= 4)
|
|
once_more = 1;
|
|
}
|
|
|
|
/* Make it clear that the dependencies of `p@{4 + i}' have been
|
|
analyzed. */
|
|
need[4 + i] = 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 2; i >= 0; i--)
|
|
{
|
|
char syll_name[32];
|
|
if (! instr->pls_present[i])
|
|
continue;
|
|
|
|
snprintf (syll_name, sizeof(syll_name), "PLS%d", i);
|
|
print_syllable (syll_name, 0, instr->pls[i]);
|
|
|
|
if (i < 2)
|
|
{
|
|
print_elp (info, 2 * i, (instr->pls[i] & 0x7f000000) >> 24,
|
|
need[2 * i]);
|
|
print_elp (info, 2 * i + 1, (instr->pls[i] & 0x007f0000) >> 16,
|
|
need[2 * i + 1]);
|
|
}
|
|
|
|
if (need[4 + i])
|
|
{
|
|
indentate ();
|
|
print_clp (info, 4 + i, instr->pls[i] & 0x0000ffff);
|
|
}
|
|
|
|
end_syllable ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_aas(disassemble_info *info, int idx)
|
|
{
|
|
static const char *names[] = {"reserved", "movab", "movah", "movaw", "movad",
|
|
"movaq", "reserved", "movaqp"};
|
|
|
|
const struct unpacked_instr *instr = &unpacked_instr;
|
|
unsigned short aa0f1 = instr->aas[idx / 2];
|
|
/* Note the idiotic order of DST0 and DST1 in AA0F1. */
|
|
unsigned short dst = (aa0f1 >> ((1 - idx % 2) * 8)) & 0xff;
|
|
unsigned short aa2f1 = instr->aas[idx + 2];
|
|
unsigned short be = (aa2f1 & 0x8000) >> 15;
|
|
unsigned short opc = (aa2f1 & 0x7000) >> 12;
|
|
unsigned short area = (aa2f1 & 0x07c0) >> 6;
|
|
unsigned short ind = (aa2f1 & 0x003e) >> 1;
|
|
unsigned short am = aa2f1 & 0x0001;
|
|
|
|
/* Follow the same strange order of fields as in LDIS. */
|
|
my_printf ("%s,%d area = %d, ind = %d, am = %d, be = %d, ",
|
|
names[opc], idx, area, ind, am, be);
|
|
|
|
print_dst (info, dst, opc < 5 ? DOUBLE : QUAD);
|
|
}
|
|
|
|
static void
|
|
print_hs (disassemble_info *info)
|
|
{
|
|
const struct unpacked_instr *instr = &unpacked_instr;
|
|
unsigned int hs = instr->hs;
|
|
/* Mimic the behaviour of LDIS. I wonder what this colon is needed for. */
|
|
print_syllable ("HS", 0, hs);
|
|
|
|
if (hs & 0x00000400)
|
|
{
|
|
indentate ();
|
|
my_printf ("loop_mode");
|
|
}
|
|
|
|
if (hs & 0x00000380)
|
|
{
|
|
unsigned int nop = (hs & 0x00000380) >> 7;
|
|
|
|
indentate ();
|
|
my_printf ("nop %d", nop);
|
|
}
|
|
|
|
end_syllable ();
|
|
}
|
|
|
|
|
|
static void
|
|
print_ctcond (disassemble_info *info, unsigned int ctcond)
|
|
{
|
|
/* I don't want to call it `ct' as it's done in C.17.1.2 of iset-vX.single
|
|
because it looks rather confusing. */
|
|
unsigned int cond_type = (ctcond & 0x1e0) >> 5;
|
|
unsigned int psrc = (ctcond & 0x01f);
|
|
|
|
/* There's no point in printing `? always' condition. */
|
|
if (cond_type == 1)
|
|
return;
|
|
|
|
my_printf (" ? ");
|
|
|
|
/* These types of conditions imply the negation of the consequent predicate
|
|
register. */
|
|
if (cond_type == 3
|
|
|| cond_type == 7
|
|
|| cond_type == 0xe)
|
|
my_printf ("~");
|
|
|
|
/* These types of conditions involve a (probably negated) predicate
|
|
register. */
|
|
if (cond_type == 2
|
|
|| cond_type == 3
|
|
|| cond_type == 6
|
|
|| cond_type == 7
|
|
|| cond_type == 0xe
|
|
|| cond_type == 0xf)
|
|
my_printf ("%%pred%d", psrc);
|
|
|
|
if (cond_type == 4
|
|
|| cond_type == 6
|
|
|| cond_type == 0xe)
|
|
{
|
|
if (cond_type == 6
|
|
|| cond_type == 0xe)
|
|
my_printf (" || ");
|
|
|
|
my_printf ("%%LOOP_END");
|
|
}
|
|
|
|
if (cond_type == 5
|
|
|| cond_type == 7
|
|
|| cond_type == 0xf)
|
|
{
|
|
if (cond_type == 7
|
|
|| cond_type == 0xf)
|
|
my_printf (" && ");
|
|
my_printf ("%%NOT_LOOP_END");
|
|
}
|
|
|
|
if (cond_type == 8)
|
|
{
|
|
my_printf ("%%MLOCK");
|
|
/* It's not clearly said in C.17.1.2 of iset-vX.single if the uppermost
|
|
fourth bit in `psrc' has any meaning at all. */
|
|
if (psrc & 0xf)
|
|
{
|
|
static const int conv[] = {0, 1, 3, 4};
|
|
int i;
|
|
|
|
my_printf (" || %%dt_al");
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (psrc & (1 << i))
|
|
my_printf ("%d", conv[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* `lock_cond || pl_cond' control transfer conditions. */
|
|
if (cond_type == 9)
|
|
{
|
|
unsigned int type = (psrc & 0x18) >> 3;
|
|
if (type == 0)
|
|
{
|
|
static const int cmp_num_to_alc[] = {0, 1, 3, 4};
|
|
unsigned int cmp_num = (psrc & 0x6) >> 1;
|
|
unsigned int neg = psrc & 0x1;
|
|
|
|
my_printf ("%%MLOCK || %s%%cmp%d", neg ? "~" : "",
|
|
cmp_num_to_alc[cmp_num]);
|
|
}
|
|
else if (type == 1)
|
|
{
|
|
unsigned int cmp_jk = (psrc & 0x4) >> 2;
|
|
unsigned int negj = (psrc & 0x2) >> 1;
|
|
unsigned int negk = psrc & 0x1;
|
|
|
|
my_printf ("%%MLOCK || %s%%cmp%d || %s%%cmp%d",
|
|
negj ? "~" : "", cmp_jk == 0 ? 0 : 3,
|
|
negk ? "~" : "", cmp_jk == 0 ? 1 : 4);
|
|
}
|
|
else if (type == 2)
|
|
{
|
|
unsigned int clp_num = (psrc & 0x6) >> 1;
|
|
unsigned int neg = psrc & 0x1;
|
|
|
|
my_printf ("%%MLOCK || %s%%clp%d", neg ? "~" : "", clp_num);
|
|
}
|
|
}
|
|
|
|
if (cond_type >= 0xa && cond_type <= 0xd)
|
|
my_printf ("<reserved condition type>");
|
|
|
|
}
|
|
|
|
static void
|
|
print_ct (disassemble_info *info, unsigned int ctop, unsigned int ctcond)
|
|
{
|
|
const struct unpacked_instr *instr = &unpacked_instr;
|
|
|
|
/* I don't want to call it `ct' as it's done in C.17.1.2 of iset-vX.single
|
|
because it looks rather confusing. */
|
|
unsigned int cond_type = (ctcond & 0x1e0) >> 5;
|
|
|
|
/* There's no point in printing any CT instruction if it's blocked by
|
|
`? never' condition. Note that there's always some sort of CT in any SS. */
|
|
if (cond_type == 0)
|
|
return;
|
|
|
|
/* This is a CALL in fact. There's no point in printing a duplicating CT. */
|
|
if (instr->cs1_present
|
|
&& ((instr->cs1 & 0xf0000000) >> 28) == 5)
|
|
return;
|
|
|
|
/* According to C.17.1.1 in iset-vX.single `SS.ctop == 0' encodes an immediate
|
|
branch (i.e. IBRANCH). Provided that this instruction requires also CS0
|
|
with IBRANCH encoded within it, it makes little sense to print it here.
|
|
Moreover, if CS0 is missing or `CS0.opc != IBRANCH' this operation should
|
|
take no effect. */
|
|
if (ctop == 0)
|
|
return;
|
|
|
|
/* According to C.17.1.1 we are left with a "prepared" `ct %ctpr<ctop={1,2,
|
|
3}>' under some condition different from "never". */
|
|
indentate ();
|
|
my_printf ("ct %%ctpr%d", ctop);
|
|
|
|
print_ctcond (info, ctcond);
|
|
}
|
|
|
|
static void
|
|
print_ss (disassemble_info *info)
|
|
{
|
|
const struct unpacked_instr *instr = &unpacked_instr;
|
|
unsigned int ss = instr->ss;
|
|
|
|
/* These fields are present both for `type == {0, 1}'. */
|
|
unsigned int ipd = (ss & 0xc0000000) >> 30;
|
|
unsigned int rp_opc_hi = (ss & 0x08000000) >> 27;
|
|
unsigned int rp_opc_lo = (ss & 0x02000000) >> 25;
|
|
unsigned int rp_opc = (rp_opc_hi << 1) | rp_opc_lo;
|
|
unsigned int type = (ss & 0x00100000) >> 20;
|
|
unsigned int ctop = (ss & 0x00000c00) >> 10;
|
|
unsigned int ctcond = (ss & 0x000001ff);
|
|
|
|
print_syllable ("SS", 0, ss);
|
|
|
|
/* When printing out SS instructions try to follow the same order as LDIS at
|
|
least for `SS.type == 0'. To find out this order I customized
|
|
`SS == 0xfffeffff' to get all possible fields in `SS.type == 0' case and
|
|
disassembled it with LDIS. */
|
|
|
|
/* CT deserves its own output function due to the complexity of CTCOND. */
|
|
print_ct (info, ctop, ctcond);
|
|
|
|
if (ipd)
|
|
{
|
|
indentate ();
|
|
my_printf ("ipd %d", ipd);
|
|
}
|
|
|
|
if (type == 0)
|
|
{
|
|
unsigned int eap = (ss & 0x20000000) >> 29;
|
|
unsigned int bap = (ss & 0x10000000) >> 28;
|
|
|
|
if (eap)
|
|
{
|
|
indentate ();
|
|
my_printf ("eap");
|
|
}
|
|
|
|
if (bap)
|
|
{
|
|
indentate ();
|
|
my_printf ("bap");
|
|
}
|
|
}
|
|
|
|
/* According to iset-v5.single this should be output both for `type = {0,1}',
|
|
shouldn't it? Beware of some changes between different versions of
|
|
instruction set related to these instructions! There used to be no CRP
|
|
field on elbrus-v1, while on elbrus-v2 SRP and CRP used to be treated as
|
|
two independent instructions. */
|
|
if (rp_opc)
|
|
{
|
|
static const char *rp_names[] = {"", "crp", "srp", "slrp"};
|
|
|
|
indentate ();
|
|
my_printf ("%s", rp_names[rp_opc]);
|
|
|
|
}
|
|
|
|
if (type == 0)
|
|
{
|
|
unsigned int vfdi = (ss & 0x04000000) >> 26;
|
|
unsigned int abg = (ss & 0x01800000) >> 23;
|
|
unsigned int abn = (ss & 0x00600000) >> 21;
|
|
unsigned int abp = (ss & 0x000c0000) >> 18;
|
|
unsigned int alc = (ss & 0x00030000) >> 16;
|
|
|
|
if (vfdi)
|
|
{
|
|
indentate ();
|
|
my_printf ("vfdi");
|
|
}
|
|
|
|
if (abg)
|
|
{
|
|
indentate ();
|
|
my_printf ("abg abgi=%d, abgd=%d", abg >> 1, abg & 0x1);
|
|
}
|
|
|
|
if (abn)
|
|
{
|
|
indentate ();
|
|
my_printf ("abn abnf=%d, abnt=%d", abn >> 1, abn & 0x1);
|
|
}
|
|
|
|
if (abp)
|
|
{
|
|
indentate ();
|
|
my_printf ("abp abpf=%d, abpt=%d", abp >> 1, abp & 0x1);
|
|
}
|
|
|
|
if (alc)
|
|
{
|
|
indentate ();
|
|
my_printf ("alc alcf=%d, alct=%d", alc >> 1, alc & 0x1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
}
|
|
|
|
end_syllable ();
|
|
}
|
|
|
|
static void
|
|
print_cs0 (disassemble_info *info, bfd_vma instr_addr)
|
|
{
|
|
typedef enum
|
|
{
|
|
NOTHING,
|
|
IBRANCH,
|
|
PREF,
|
|
PUTTSD,
|
|
DONE,
|
|
HRET,
|
|
GLAUNCH,
|
|
DISP,
|
|
SDISP,
|
|
GETTSD,
|
|
LDISP,
|
|
RETURN
|
|
} cs0_type;
|
|
|
|
static const char *cs0_names[] = {"", "ibranch", "pref", "puttsd",
|
|
"done", "hret", "glaunch", "disp", "sdisp",
|
|
"gettsd", "ldisp", "return"};
|
|
/* This is a copy of Table B.4.1 in `iset-v6.single'. */
|
|
static cs0_type cs0_ops[4][4] = {
|
|
{IBRANCH, PREF, PUTTSD, DONE},
|
|
{DISP, NOTHING, SDISP, GETTSD},
|
|
{DISP, LDISP, SDISP, GETTSD},
|
|
{DISP, NOTHING, SDISP, RETURN}
|
|
};
|
|
const struct unpacked_instr *instr = &unpacked_instr;
|
|
unsigned int cs0 = instr->cs0;
|
|
|
|
/* Note that `ctpr' and `ctp_opc' fields are present both in C0F1 and
|
|
C0F2. */
|
|
unsigned int ctpr = (cs0 & 0xc0000000) >> 30;
|
|
unsigned int ctp_opc = (cs0 & 0x30000000) >> 28;
|
|
unsigned int param_type = (cs0 & 0x00000007);
|
|
cs0_type type = cs0_ops[ctpr][ctp_opc];
|
|
|
|
if (type == RETURN && param_type == 1)
|
|
type = GETTSD;
|
|
else if (type == DONE)
|
|
{
|
|
if (param_type == 3)
|
|
type = HRET;
|
|
else if (param_type == 4)
|
|
type = GLAUNCH;
|
|
}
|
|
|
|
|
|
print_syllable ("CS0", 0, instr->cs0);
|
|
|
|
if (type == IBRANCH || type == DONE || type == HRET || type == GLAUNCH)
|
|
{
|
|
/* IBRANCH, DONE, HRET and GLAUNCH are special because they require SS
|
|
to be properly encoded. */
|
|
if (! instr->ss_present
|
|
/* SS.ctop should be equal to zero for IBRANCH, DONE, HRET and
|
|
GLAUNCH (see C.17.1.1, note that they don't mention the latter two
|
|
instructions there which is probably an omission ). */
|
|
|| (instr->ss & 0x00000c00))
|
|
{
|
|
indentate ();
|
|
my_printf ("invalid %s", cs0_names[type]);
|
|
}
|
|
/* Don't output either of the aforementioned instructions under "never"
|
|
condition. Don't disassemble CS0 being a part of HCALL. Unlike ldis
|
|
HCALL is currently disassembled on behalf of CS1. */
|
|
else if (instr->ss & 0x1ff
|
|
&& !(instr->cs1_present
|
|
/* CS1.opc == CALL */
|
|
&& (instr->cs1 & 0xf0000000) >> 28 == 5
|
|
/* CS1.param.ctopc == HCALL */
|
|
&& (instr->cs1 & 0x380) >> 7 == 2))
|
|
{
|
|
indentate ();
|
|
my_printf ("%s", cs0_names[type]);
|
|
|
|
if (type == IBRANCH)
|
|
{
|
|
/* C0F2 has `disp' field. In `C0F1' it's called `param'. Is this
|
|
the only difference between these two formats? Funnily enough,
|
|
DONE is also C0F2 and thus has `disp', though it obviously
|
|
makes no sense for it. */
|
|
unsigned int disp = (cs0 & 0x0fffffff);
|
|
/* Calculate a signed displacement in bytes. I wonder if it
|
|
should be actually printed out. */
|
|
int sdisp = ((int) (disp << 4)) >> 1;
|
|
/* FIXME: this way I ensure that it'll work correctly
|
|
both on 32 and 64-bit hosts. */
|
|
my_printf (" 0x%llx", (unsigned long long) (instr_addr + sdisp));
|
|
}
|
|
|
|
print_ctcond (info, instr->ss & 0x1ff);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
indentate ();
|
|
/* Note that according to Table B.4.1 it's possible to obtain
|
|
`gettsd %ctpr{1,2} with an invalid value for CS0.param.type. */
|
|
if (type == GETTSD && param_type != 1)
|
|
my_printf ("invalid ");
|
|
|
|
my_printf ("%s ", cs0_names[type]);
|
|
|
|
if (type == DISP
|
|
|| type == SDISP
|
|
|| type == LDISP
|
|
/* Note that RETURN is said to be COPF1. I can't understand what its
|
|
`CS0.param' is needed for: all of the bits except the three
|
|
lowermost ones are undefined, while the latter also known as "type"
|
|
field should be filled in with zeroes. */
|
|
|| type == RETURN
|
|
/* GETTSD has as meaningless `CS0.param' as RETURN. The only
|
|
difference is that its `CS0.param.type' should be equal to `1'. I
|
|
wonder if I should check for that and output something like
|
|
"invalid gettsd" if this turns out not to be the case . . . */
|
|
|| type == GETTSD)
|
|
my_printf ("%%ctpr%d", ctpr);
|
|
|
|
if (type == SDISP)
|
|
my_printf (", 0x%x", cs0 & 0x1f);
|
|
else if (type == DISP
|
|
|| type == LDISP
|
|
|| type == PUTTSD)
|
|
{
|
|
unsigned int disp = (cs0 & 0x0fffffff);
|
|
int sgnd_disp = ((int) (disp << 4)) >> 1;
|
|
/* PUTTSD obviously doesn't take %ctpr{j} parameter. TODO: beware of
|
|
an optional predicate which may control its execution which is
|
|
encoded via `SS.ctcond.psrc' and `SS.ts_opc == PUTTSDC{P,N}' in
|
|
case of `SS.type == 1' (see C.21.4). I wonder if `ct %ctpr<j>'
|
|
encoded in `SS.ctop' under the same `SS.ctcond' takes an effect in
|
|
such a case. */
|
|
my_printf ("%s0x%llx", type == PUTTSD ? "" : ", ",
|
|
/* FIXME: this way I ensure that it'll work correctly
|
|
both on 32 and 64-bit hosts. */
|
|
(unsigned long long) (instr_addr + sgnd_disp));
|
|
}
|
|
|
|
if (type == PREF)
|
|
{
|
|
/* LDIS doesn't currently bother about producing a target label for
|
|
PREF, it just stupidly dumps its parameters in their immediate
|
|
form. Do the same for now. */
|
|
|
|
unsigned int pdisp = (instr->cs0 & 0x0ffffff0) >> 4;
|
|
unsigned int ipd = (instr->cs0 & 0x00000008) >> 3;
|
|
unsigned int prefr = instr->cs0 & 0x00000007;
|
|
|
|
my_printf ("%%ipr%d, disp = 0x%x, ipd = %d", prefr, pdisp, ipd);
|
|
}
|
|
}
|
|
|
|
end_syllable ();
|
|
}
|
|
|
|
static void
|
|
print_cs1 (disassemble_info *info)
|
|
{
|
|
enum {
|
|
SETR0,
|
|
SETR1,
|
|
SETEI,
|
|
WAIT,
|
|
SETBR,
|
|
CALL,
|
|
MAS_OPC,
|
|
FLUSHR,
|
|
BG
|
|
};
|
|
|
|
const struct unpacked_instr *instr = &unpacked_instr;
|
|
unsigned int cs1 = instr->cs1;
|
|
unsigned int opc = (cs1 & 0xf0000000) >> 28;
|
|
|
|
print_syllable ("CS1", 0, cs1);
|
|
|
|
if (opc == SETR0
|
|
|| opc == SETR1
|
|
|| opc == SETBR)
|
|
{
|
|
unsigned int setbp = (cs1 & 0x08000000) >> 27;
|
|
unsigned int setbn = (cs1 & 0x04000000) >> 26;
|
|
|
|
/* Try to follow the same order of these instructions as in LDIS.
|
|
Presumably `vfrpsz' should come first, while `setbp' should be placed
|
|
between `setwd' and `setbn', but this is to be verified. I don't have
|
|
a binary with these commands by hand right now. */
|
|
|
|
if (opc == SETR1)
|
|
{
|
|
if (! instr->lts_present[0])
|
|
{
|
|
indentate ();
|
|
my_printf ("<bogus vfrpsz>");
|
|
}
|
|
else
|
|
{
|
|
/* Find out if VFRPSZ is always encoded together with SETWD. This
|
|
seems to be the case even if no SETWD has been explicitly
|
|
specified. */
|
|
unsigned int rpsz = (instr->lts[0] & 0x0001f000) >> 12;
|
|
indentate ();
|
|
my_printf ("vfrpsz rpsz = 0x%x", rpsz);
|
|
}
|
|
}
|
|
|
|
if (opc == SETR0
|
|
|| opc == SETR1)
|
|
{
|
|
if (! instr->lts_present[0])
|
|
{
|
|
indentate ();
|
|
my_printf ("<bogus setwd>");
|
|
}
|
|
else
|
|
{
|
|
unsigned int lts0 = instr->lts[0];
|
|
unsigned int wsz = (lts0 & 0x00000fe0) >> 5;
|
|
unsigned int nfx = (lts0 & 0x00000010) >> 4;
|
|
|
|
indentate ();
|
|
my_printf ("setwd wsz = 0x%x, nfx = 0x%x", wsz, nfx);
|
|
|
|
if (mcpu >= 3)
|
|
{
|
|
/* DBL parameter of SETWD was added only starting from
|
|
elbrus-v3. */
|
|
unsigned int dbl = (lts0 & 0x00000008) >> 3;
|
|
my_printf (", dbl = 0x%x", dbl);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (setbn)
|
|
{
|
|
unsigned int rcur = (cs1 & 0x0003f000) >> 12;
|
|
unsigned int rsz = (cs1 & 0x00000fc0) >> 6;
|
|
unsigned int rbs = cs1 & 0x0000003f;
|
|
|
|
indentate ();
|
|
my_printf ("setbn rbs = 0x%x, rsz = 0x%x, rcur = 0x%x",
|
|
rbs, rsz, rcur);
|
|
}
|
|
|
|
if (setbp)
|
|
{
|
|
unsigned int psz = (cs1 & 0x007c0000) >> 18;
|
|
|
|
indentate ();
|
|
my_printf ("setbp psz = 0x%x", psz);
|
|
}
|
|
}
|
|
else if (opc == SETEI)
|
|
{
|
|
/* Verify that CS1.param.sft = CS1.param[27] is equal to zero as required
|
|
in C.14.3. */
|
|
unsigned int sft = (cs1 & 0x08000000) >> 27;
|
|
unsigned int eir = (cs1 & 0x000000ff);
|
|
|
|
indentate ();
|
|
if (sft)
|
|
my_printf ("%s", mcpu >= 2 ? "setsft" : "unimp");
|
|
else
|
|
my_printf ("setei 0x%x", eir);
|
|
}
|
|
else if (opc == WAIT)
|
|
{
|
|
unsigned int ma_c = (cs1 & 0x00000020) >> 5;
|
|
unsigned int fl_c = (cs1 & 0x00000010) >> 4;
|
|
unsigned int ld_c = (cs1 & 0x00000008) >> 3;
|
|
unsigned int st_c = (cs1 & 0x00000004) >> 2;
|
|
unsigned int all_e = (cs1 & 0x00000002) >> 1;
|
|
unsigned int all_c = cs1 & 0x00000001;
|
|
|
|
indentate ();
|
|
my_printf ("wait ");
|
|
|
|
if (mcpu >= 5)
|
|
{
|
|
/* `sa{l,s}' fields are `elbrus-v5'-specific. Each of them makes sense
|
|
only in the presence of `{ld,st}_c == 1' respectively. */
|
|
if (ld_c)
|
|
{
|
|
unsigned int sal = (cs1 & 0x00000100) >> 8;
|
|
my_printf ("sal = %d, ", sal);
|
|
}
|
|
|
|
if (st_c)
|
|
{
|
|
unsigned int sas = (cs1 & 0x00000080) >> 7;
|
|
my_printf ("sas = %d, ", sas);
|
|
}
|
|
}
|
|
|
|
if (mcpu >= 2)
|
|
{
|
|
/* `trap' field was introduced starting from `elbrus-v2'. */
|
|
unsigned int trap = (cs1 & 0x00000040) >> 6;
|
|
my_printf ("trap = %d, ", trap);
|
|
}
|
|
|
|
my_printf ("ma_c = %d, fl_c = %d, ld_c = %d, st_c = %d, all_e = %d, "
|
|
"all_c = %d", ma_c, fl_c, ld_c, st_c, all_e, all_c);
|
|
}
|
|
else if (opc == CALL)
|
|
{
|
|
unsigned int ctop = (instr->ss & 0x00000c00) >> 10;
|
|
/* In C.17.4 it's said that other bits in CS1.param except for the
|
|
seven lowermost ones are ignored. */
|
|
unsigned int wbs = cs1 & 0x7f;
|
|
|
|
indentate ();
|
|
if (ctop)
|
|
{
|
|
my_printf ("call %%ctpr%d, wbs = 0x%x", ctop, wbs);
|
|
print_ctcond (info, instr->ss & 0x1ff);
|
|
}
|
|
else
|
|
{
|
|
unsigned int cs1_ctopc = (cs1 & 0x380) >> 7;
|
|
/* CS1.param.ctpopc == HCALL. CS0 is required to encode HCALL. */
|
|
if (cs1_ctopc == 2 && instr->cs0_present)
|
|
{
|
|
unsigned int cs0 = instr->cs0;
|
|
unsigned int cs0_opc = (cs0 & 0xf0000000) >> 28;
|
|
/* CS0.opc == HCALL, which means
|
|
CS0.opc.ctpr == CS0.opc.ctp_opc == 0 */
|
|
if (cs0_opc == 0)
|
|
{
|
|
unsigned int hdisp = (cs0 & 0x1e) >> 1;
|
|
my_printf ("hcall 0x%x, wbs = 0x%x", hdisp, wbs);
|
|
print_ctcond (info, instr->ss & 0x1ff);
|
|
}
|
|
}
|
|
else
|
|
my_printf ("<bogus call>");
|
|
}
|
|
}
|
|
else if (opc == MAS_OPC)
|
|
{
|
|
#if defined ENABLE_E2K_ENCODINGS
|
|
/* Note that LDIS doesn't print it out as a standalone instruction. */
|
|
unsigned int mas = cs1 & 0x0fffffff;
|
|
|
|
indentate ();
|
|
my_printf ("mas 0x%x", mas);
|
|
#endif /* ENABLE_E2K_ENCODINGS */
|
|
}
|
|
else if (opc == FLUSHR)
|
|
{
|
|
/* . . . these stupid engineers off! FLUSHR seems to be responsible for
|
|
encoding both `flushr' and `flushc'. Moreover, according to their
|
|
logic it should be possible to encode them simultaneously. */
|
|
|
|
/* Check for `CS1.param.flr'. */
|
|
if (cs1 & 0x00000001)
|
|
{
|
|
indentate ();
|
|
my_printf ("flushr");
|
|
}
|
|
|
|
/* Check for `CS1.param.flc'. */
|
|
if (cs1 & 0x00000002)
|
|
{
|
|
indentate ();
|
|
my_printf ("flushc");
|
|
}
|
|
}
|
|
else if (opc == BG)
|
|
{
|
|
/* Hopefully, `vfbg' is the only instruction encoded by BG. I'm currently
|
|
unable to find other ones in `iset-v5.single' at least . . . */
|
|
unsigned int chkm4 = (cs1 & 0x00010000) >> 16;
|
|
unsigned int dmask = (cs1 & 0x0000ff00) >> 8;
|
|
unsigned int umsk = cs1 & 0x000000ff;
|
|
|
|
indentate ();
|
|
/* Print its fields in the order proposed in C.14.10. */
|
|
my_printf ("vfbg umask = 0x%x, dmask = 0x%x, chkm4 = 0x%x",
|
|
umsk, dmask, chkm4);
|
|
}
|
|
else
|
|
{
|
|
indentate ();
|
|
my_printf ("unimp");
|
|
}
|
|
|
|
end_syllable ();
|
|
}
|
|
|
|
|
|
#if defined ENABLE_E2K_ENCODINGS
|
|
static void
|
|
print_cds (disassemble_info *info, int idx)
|
|
{
|
|
int i;
|
|
const struct unpacked_instr *instr = &unpacked_instr;
|
|
/* FIXME: this approach is not safe from the point of view of endianness. */
|
|
unsigned short *cds = (unsigned short *) &instr->cds[idx];
|
|
char syll_name[5];
|
|
|
|
snprintf (syll_name, 5, "CDS%d", idx);
|
|
print_syllable (syll_name, 0, instr->cds[idx]);
|
|
|
|
|
|
for (i = 1; i >= 0; i--)
|
|
{
|
|
unsigned short mask = (cds[i] & 0x3c00) >> 10;
|
|
|
|
if (mask == 0)
|
|
continue;
|
|
|
|
unsigned short opc = (cds[i] & 0xc000) >> 14;
|
|
unsigned short neg = (cds[i] & 0x0380) >> 7;
|
|
unsigned short pred = cds[i] & 0x7f;
|
|
unsigned short num = pred & 0x1f;
|
|
|
|
indentate ();
|
|
my_printf ("%s,cd%d%d ", opc < 2 ? "rlp" : "mrgc", idx, 1 - i);
|
|
|
|
|
|
if ((pred & 0x60) == 0x40)
|
|
my_printf ("%%pcnt%hd", num);
|
|
else if ((pred & 0x60) == 0x60)
|
|
my_printf ("%%pred%hd", num);
|
|
else
|
|
my_printf ("<invalid predicate>");
|
|
|
|
int j;
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
if (mask & (1 << j))
|
|
{
|
|
int alc = ((opc & 0x1) ? 3 : 0) + j;
|
|
my_printf (", %s>alc%d", (neg & (1 << j)) ? "~" : "", alc);
|
|
}
|
|
}
|
|
}
|
|
|
|
end_syllable ();
|
|
}
|
|
|
|
#endif /* ENABLE_E2K_ENCODINGS */
|
|
|
|
static void
|
|
print_instr_e2k (bfd_vma instr_addr, disassemble_info *info)
|
|
{
|
|
int i;
|
|
const struct unpacked_instr *instr = &unpacked_instr;
|
|
|
|
my_printf ("\n");
|
|
|
|
print_hs (info);
|
|
|
|
if (instr->ss_present)
|
|
print_ss (info);
|
|
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
if (instr->als_present[i])
|
|
print_alf (info, i);
|
|
}
|
|
|
|
if (instr->cs0_present)
|
|
print_cs0 (info, instr_addr);
|
|
|
|
/* Note that `ALES{5,2}' are always marked as allocated together, so it's
|
|
enough to check for only one of them. */
|
|
if ((instr->ales_present[5] & 0x2) == 0x2)
|
|
{
|
|
/* Don't print the name of either of `ALES{5,2}' unless it has been marked
|
|
as present in HS. However, always print the contents of these syllables
|
|
if they are allocated. */
|
|
print_syllable ((instr->ales_present[2] & 0x1) ? "ALES2" : "", 1,
|
|
instr->ales[2]);
|
|
end_syllable ();
|
|
|
|
print_syllable ((instr->ales_present[5] & 0x1) ? "ALES5" : "", 1,
|
|
instr->ales[5]);
|
|
end_syllable ();
|
|
}
|
|
|
|
if (instr->cs1_present)
|
|
print_cs1 (info);
|
|
|
|
for (i = 0; i < 5; i++)
|
|
{
|
|
/* We've already taken care of ALES2 above. */
|
|
if (i == 2)
|
|
continue;
|
|
|
|
if (instr->ales_present[i])
|
|
{
|
|
char syll_name[6];
|
|
snprintf (syll_name, 6, "ALES%d", i);
|
|
print_syllable (syll_name, 1, instr->ales[i]);
|
|
end_syllable ();
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
if (instr->aas_present[i])
|
|
{
|
|
char syll_name[5];
|
|
snprintf (syll_name, 5, "AAS%d", i);
|
|
print_syllable (syll_name, 1, instr->aas[i]);
|
|
|
|
if (i >= 2)
|
|
{
|
|
indentate ();
|
|
print_aas (info, i - 2);
|
|
}
|
|
|
|
end_syllable ();
|
|
}
|
|
}
|
|
|
|
if (instr->half_gap_present)
|
|
{
|
|
print_syllable ("GAP", 1, instr->half_gap);
|
|
end_syllable ();
|
|
}
|
|
|
|
for (i = 0; i < 16 && instr->gap_present[i] != 0; i++)
|
|
{
|
|
if (instr->gap_present[i])
|
|
{
|
|
print_syllable ("GAP", 0, instr->gap[i]);
|
|
end_syllable ();
|
|
}
|
|
}
|
|
|
|
for (i = 3; i >= 0; i--)
|
|
{
|
|
if (instr->lts_present[i])
|
|
{
|
|
char syll_name[32];
|
|
snprintf (syll_name, sizeof(syll_name), "LTS%d", i);
|
|
print_syllable (syll_name, 0, instr->lts[i]);
|
|
end_syllable ();
|
|
}
|
|
}
|
|
|
|
print_pls_syllables (info);
|
|
|
|
#if defined ENABLE_E2K_ENCODINGS
|
|
/* Some people believe that CDSes are redundant in "release" mode (see
|
|
Bug #88977). */
|
|
for (i = 2; i >= 0; i--)
|
|
{
|
|
if (instr->cds_present[i])
|
|
print_cds (info, i);
|
|
}
|
|
#endif /* !defined ENABLE_E2K_ENCODINGS */
|
|
}
|
|
|
|
static void
|
|
print_apb_instr (disassemble_info *info)
|
|
{
|
|
int i;
|
|
const struct unpacked_instr *instr = &unpacked_instr;
|
|
|
|
my_printf ("\n");
|
|
|
|
/* It seems that API_L should be always followed by API_R according to 6.8 in
|
|
`iset-vX.single'. However, because it's not stated there for certain, one
|
|
may still doubt . . . If that wasn't the case, however, how would
|
|
disassembler distinguish them? */
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
const unsigned int *api = i == 0 ? instr->api_l : instr->api_r;
|
|
unsigned int ct_or_dpl = (api[0] & 0x80000000) >> 31;
|
|
unsigned int si = (api[0] & 0x40000000) >> 30;
|
|
unsigned int fmt = (api[0] & 0x0e000000) >> 25;
|
|
|
|
print_syllable (i == 0 ? "API_L" : "API_R", 0, api[0]);
|
|
|
|
indentate ();
|
|
my_printf ("apb %s=%d", i == 0 ? "ct" : "dpl", ct_or_dpl);
|
|
|
|
/* FIXME: in iset-vX.single they refer to `fmt == "none"'. However,
|
|
"none" which is typically believed to be 0xc0 across this document
|
|
can't be obviously encoded in 3 bytes. For now stupidly believe that
|
|
it means zero in context of APB. */
|
|
if (mcpu <= 1 && fmt != 0)
|
|
/* "Secondary index access" was removed starting from elbrus-v2. */
|
|
my_printf (", si=%d", si);
|
|
|
|
if (fmt != 0)
|
|
{
|
|
unsigned int dcd = (api[0] & 0x30000000) >> 28;
|
|
my_printf (", dcd=%d", dcd);
|
|
}
|
|
|
|
my_printf (", fmt=%d", fmt);
|
|
|
|
if (fmt != 0)
|
|
{
|
|
unsigned int mrng = (api[0] & 0x01f00000) >> 20;
|
|
unsigned int d = (api[0] & 0x000f8000) >> 15;
|
|
unsigned int asz = (api[0] & 0x000000e0) >> 5;
|
|
/* I don't call it `abs' as it's called in `iset-vX.single' since
|
|
gcc-4.6.3 and more recent ones complain that it shadows the global
|
|
`abs ()' function provided that `-Wshadow' is supplied. */
|
|
unsigned int area_base = (api[0] & 0x0000001f);
|
|
|
|
my_printf (", mrng=%d", mrng);
|
|
my_printf (", d=%d", d);
|
|
|
|
/* APS.si makes no sense for `elbrus-v{X>=2}'. */
|
|
if (mcpu >= 2 || si == 0)
|
|
{
|
|
unsigned int incr = (api[0] & 0x00007000) >> 12;
|
|
unsigned int ind = (api[0] & 0x00000f00) >> 8;
|
|
|
|
my_printf (", incr=%d", incr);
|
|
my_printf (", ind=%d", ind);
|
|
|
|
}
|
|
else
|
|
{
|
|
unsigned int be = (api[0] & 0x00004000) >> 14;
|
|
unsigned int am = (api[0] & 0x00002000) >> 13;
|
|
unsigned int area = (api[0] & 0x00001f00) >> 8;
|
|
|
|
my_printf (", be=%d", be);
|
|
my_printf (", am=%d", am);
|
|
my_printf (", area=%d", area);
|
|
}
|
|
|
|
my_printf (", asz=%d", asz);
|
|
my_printf (", abs=%d", area_base);
|
|
|
|
/* FIXME: should DISP be printed for `fmt="none"'? */
|
|
my_printf (", disp=0x%x", api[1]);
|
|
}
|
|
|
|
end_syllable ();
|
|
|
|
/* Print the second syllable in a pair. */
|
|
print_syllable ("", 0, api[1]);
|
|
end_syllable ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
build_hash_table (void)
|
|
{
|
|
/* FIXME: this way I temporarely get rid of the fields which are inappropriate
|
|
to disassembler. */
|
|
#define parse_alf_args NULL
|
|
#define merge_alopf_simple NULL
|
|
#define merge_alopf11 NULL
|
|
#include "e2k-opc.h"
|
|
|
|
if (build_hash_table_stage == 0)
|
|
{
|
|
if (hash_buf != NULL)
|
|
free (hash_buf);
|
|
|
|
memset (opcode_hash_table, 0, sizeof (opcode_hash_table));
|
|
hash_buf = malloc (sizeof (* hash_buf) * num_opcodes);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/* Print one instruction from MEMADDR on INFO->STREAM. */
|
|
int
|
|
print_insn_e2k (bfd_vma memaddr, disassemble_info *info)
|
|
{
|
|
bfd_byte buffer[256];
|
|
int status;
|
|
size_t command_length;
|
|
static int initialized;
|
|
|
|
/* This lets me stupidly shut Bug #88528 up for now . . . */
|
|
if (! initialized
|
|
/* Recall how `bfd_arch_info_type.mach' is set for E2K. */
|
|
|| info->mach / 3 != mcpu)
|
|
{
|
|
mcpu = info->mach / 3;
|
|
|
|
build_hash_table_stage = 0;
|
|
num_opcodes = 0;
|
|
build_hash_table ();
|
|
|
|
build_hash_table_stage = 1;
|
|
num_opcodes = 0;
|
|
build_hash_table ();
|
|
|
|
initialized = 1;
|
|
}
|
|
|
|
/* Don't even try to disassemble if the available address range doesn't fit
|
|
the most short possible instruction or is not properly aligned. */
|
|
if (info->stop_vma - memaddr < 8
|
|
|| (memaddr & 0x7) != 0)
|
|
{
|
|
/* Skip remaining bytes. Hopefully, we'll resume disassembly starting
|
|
from a more meaningful symbol. */
|
|
command_length = info->stop_vma - memaddr;
|
|
info->bytes_per_line = command_length;
|
|
return command_length;
|
|
}
|
|
|
|
/* I can't read instruction bytes with a reserve here since this may cause
|
|
a memory access error. Therefore, read just one byte . . . */
|
|
status = (*info->read_memory_func) (memaddr, buffer, 1, info);
|
|
if (status != 0)
|
|
{
|
|
(*info->memory_error_func) (status, memaddr, info);
|
|
return -1;
|
|
}
|
|
|
|
/* . . . and use it to determine the current instruction's length as if it
|
|
were synchronous. FIXME: what's going to happen if it's not? Previously
|
|
when reading bytes with a surplus I used `UnpackInstruction' to determine
|
|
COMMAND_LENGTH. I wonder if this function is capable of distinguishing
|
|
between synchronous and asynchronous instructions . . . */
|
|
command_length = (((buffer[0] >> 4) & 0x7) + 1) * 2;
|
|
command_length *= 4;
|
|
|
|
/* FIXME: the underlying condition makes no sense provided that
|
|
COMMAND_LENGTH is bounded with 64 bytes. */
|
|
if (command_length > sizeof (buffer))
|
|
return -1;
|
|
|
|
if (/* Is there enough space in the available address range to hold a
|
|
presumable synchronous instruction? */
|
|
info->stop_vma - memaddr >= command_length
|
|
/* If so, read its remaining bytes. */
|
|
&& (*info->read_memory_func) (memaddr + 1, &buffer[1],
|
|
command_length - 1, info) == 0
|
|
/* If that succeeded, try to unpack it. In case of success, it's a valid
|
|
synchronous instruction. */
|
|
&& unpack_instr (buffer))
|
|
print_instr_e2k (memaddr, info);
|
|
else if (info->stop_vma - memaddr < 16)
|
|
{
|
|
/* There's no point in trying to interpret this bogus sequence of bytes
|
|
as an APB instruction if it's not sufficiently long. */
|
|
command_length = info->stop_vma - memaddr;
|
|
info->bytes_per_line = command_length;
|
|
return command_length;
|
|
}
|
|
else
|
|
{
|
|
struct unpacked_instr *instr = &unpacked_instr;
|
|
|
|
/* Note that the read above may have failed if we mistakenly tried to
|
|
load a too lengthy invalid synchronous instruction. Therefore, re-read
|
|
everything from the very beginning. */
|
|
|
|
command_length = 16;
|
|
status = (*info->read_memory_func) (memaddr, buffer, 16, info);
|
|
|
|
if (status != 0)
|
|
{
|
|
(*info->memory_error_func) (status, memaddr, info);
|
|
return -1;
|
|
}
|
|
|
|
memcpy (instr->api_l, &buffer[0], 8);
|
|
memcpy (instr->api_r, &buffer[8], 8);
|
|
|
|
print_apb_instr (info);
|
|
}
|
|
|
|
|
|
/* When disassembling via OBJDUMP the latter prints out instruction bytes
|
|
"by default" unlike GDB. This setting makes it print all of them in a
|
|
single line unless `--insn-width=X' option has been explicitly specified.
|
|
Without that `X = 4' bytes will be printed per line by default and each
|
|
quad will be prefixed by its `ADDRESS:' which will make OBJDUMP output
|
|
unrecognizable by `linux-mcst-3.14/scripts/recordmcount.pl' (that was one
|
|
of the reasons for Bug #85734). */
|
|
info->bytes_per_line = command_length;
|
|
return command_length;
|
|
}
|
|
|