old-cross-binutils/opcodes/txvu-opc.c
1998-01-28 02:20:20 +00:00

2722 lines
75 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Opcode table for the TXVU
Copyright 1998 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program 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.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "ansidecl.h"
#include "sysdep.h"
#include "dis-asm.h"
#include "opcode/txvu.h"
#ifndef NULL
#define NULL 0
#endif
#if defined (__STDC__) || defined (ALMOST_STDC)
#define XCONCAT2(a,b) a##b
#else
#define XCONCAT2(a,b) a/**/b
#endif
#define CONCAT2(a,b) XCONCAT2(a,b)
typedef struct {
int value;
const char *name;
} keyword;
static int lookup_keyword_value PARAMS ((const keyword *, const char *, int));
static const char *lookup_keyword_name PARAMS ((const keyword *table, int));
static char *scan_symbol PARAMS ((char *));
/* Return non-zero if CH is a character that may appear in a symbol. */
/* FIXME: This will need revisiting. */
#define issymchar(ch) (isalnum (ch) || ch == '_')
#define SKIP_BLANKS(var) while (isspace (*(var))) ++(var)
/* ??? One can argue it's preferable to have the PARSE_FN support in tc-txvu.c
and the PRINT_FN support in txvu-dis.c. For this project I like having
them all in one place. */
#define PARSE_FN(fn) \
static long CONCAT2 (parse_,fn) \
PARAMS ((const txvu_opcode *, const txvu_operand *, int, char **, \
const char **));
#define INSERT_FN(fn) \
static void CONCAT2 (insert_,fn) \
PARAMS ((const txvu_opcode *, const txvu_operand *, int, TXVU_INSN *, \
long, const char **))
#define EXTRACT_FN(fn) \
static long CONCAT2 (extract_,fn) \
PARAMS ((const txvu_opcode *, const txvu_operand *, int, TXVU_INSN *, \
int *));
#define PRINT_FN(fn) \
static void CONCAT2 (print_,fn) \
PARAMS ((const txvu_opcode *, const txvu_operand *, int, TXVU_INSN *, \
disassemble_info *, long));
PARSE_FN (dotdest);
INSERT_FN (dotdest);
EXTRACT_FN (dotdest);
PRINT_FN (dotdest);
PARSE_FN (dotdest1);
PARSE_FN (dest1);
PRINT_FN (dest1);
PARSE_FN (bc);
EXTRACT_FN (bc);
PRINT_FN (sdest);
PARSE_FN (vfreg);
PRINT_FN (vfreg);
PARSE_FN (bcftreg);
PRINT_FN (bcftreg);
PARSE_FN (accdest);
PRINT_FN (accdest);
INSERT_FN (xyz);
PARSE_FN (ireg);
PRINT_FN (ireg);
PARSE_FN (freg);
PRINT_FN (freg);
PARSE_FN (ffstreg);
INSERT_FN (ffstreg);
EXTRACT_FN (ffstreg);
PRINT_FN (ffstreg);
PARSE_FN (vi01);
PRINT_FN (vi01);
INSERT_FN (luimm12);
EXTRACT_FN (luimm12);
INSERT_FN (luimm12up6);
INSERT_FN (luimm15);
EXTRACT_FN (luimm15);
/* Various types of TXVU operands, including insn suffixes.
Fields are:
BITS SHIFT FLAGS PARSE_FN INSERT_FN EXTRACT_FN PRINT_FN
Operand values are 128 + table index. This allows ASCII chars to be
included in the syntax spec. */
const txvu_operand txvu_operands[] =
{
/* place holder (??? not sure if needed) */
#define UNUSED 128
{ 0 },
/* Operands that exist in the same place for essentially the same purpose
in both upper and lower instructions. These don't have a U or L prefix.
Operands specific to the upper or lower instruction are so prefixed. */
/* Destination indicator attached to mnemonic, with leading '.' or '/'.
After parsing this, the value is stored in global `dest' so that the
register parser can verify the same choice of xyzw is used. */
#define DOTDEST (UNUSED + 1)
{ 4, TXVU_SHIFT_DEST, TXVU_OPERAND_SUFFIX,
parse_dotdest, insert_dotdest, extract_dotdest, print_dotdest },
/* ft reg, with vector specification same as DOTDEST */
#define VFTREG (DOTDEST + 1)
{ 5, TXVU_SHIFT_TREG, 0, parse_vfreg, 0, 0, print_vfreg },
/* fs reg, with vector specification same as DOTDEST */
#define VFSREG (VFTREG + 1)
{ 5, TXVU_SHIFT_SREG, 0, parse_vfreg, 0, 0, print_vfreg },
/* fd reg, with vector specification same as DOTDEST */
#define VFDREG (VFSREG + 1)
{ 5, TXVU_SHIFT_DREG, 0, parse_vfreg, 0, 0, print_vfreg },
/* Upper word operands. */
/* broadcast */
#define UBC (VFDREG + 1)
{ 2, 0, TXVU_OPERAND_SUFFIX, parse_bc, 0, extract_bc, print_sdest },
/* ftreg in broadcast case */
#define UBCFTREG (UBC + 1)
{ 5, TXVU_SHIFT_TREG, 0, parse_bcftreg, 0, 0, print_bcftreg },
/* accumulator dest */
#define UACCDEST (UBCFTREG + 1)
{ 0, 0, 0, parse_accdest, 0, 0, print_accdest },
/* The XYZ operand is a fake one that is used to ensure only "xyz" is
specified. It simplifies the opmula and opmsub entries. */
#define UXYZ (UACCDEST + 1)
{ 0, 0, TXVU_OPERAND_FAKE, 0, insert_xyz, 0, 0 },
/* Lower word operands. */
/* 5 bit signed immediate. */
#define LIMM5 (UXYZ + 1)
{ 5, 6, TXVU_OPERAND_SIGNED, 0, 0, 0, 0 },
/* 11 bit signed immediate. */
#define LIMM11 (LIMM5 + 1)
{ 11, 0, TXVU_OPERAND_SIGNED, 0, 0, 0, 0 },
/* 15 bit unsigned immediate. */
#define LUIMM15 (LIMM11 + 1)
{ 15, 0, 0, 0, insert_luimm15, extract_luimm15, 0 },
/* ID register. */
#define LIDREG (LUIMM15 + 1)
{ 5, 6, 0, parse_ireg, 0, 0, print_ireg },
/* IS register. */
#define LISREG (LIDREG + 1)
{ 5, 11, 0, parse_ireg, 0, 0, print_ireg },
/* IT register. */
#define LITREG (LISREG + 1)
{ 5, 16, 0, parse_ireg, 0, 0, print_ireg },
/* FS reg, with FSF field selector. */
#define LFSFFSREG (LITREG + 1)
{ 5, 11, 0, parse_ffstreg, insert_ffstreg, extract_ffstreg, print_ffstreg },
/* FS reg, no selector (choice of x,y,z,w is provided by opcode). */
#define LFSREG (LFSFFSREG + 1)
{ 5, 11, 0, parse_freg, 0, 0, print_freg },
/* FT reg, with FTF field selector. */
#define LFTFFTREG (LFSREG + 1)
{ 5, 16, 0, parse_ffstreg, insert_ffstreg, extract_ffstreg, print_ffstreg },
/* VI01 register. */
#define LVI01 (LFTFFTREG + 1)
{ 0, 0, 0, parse_vi01, 0, 0, print_vi01 },
/* 24 bit unsigned immediate. */
#define LUIMM24 (LVI01 + 1)
{ 24, 0, 0, 0, 0, 0, 0 },
/* 12 bit unsigned immediate, split into 1 and 11 bit pieces. */
#define LUIMM12 (LUIMM24 + 1)
{ 12, 0, 0, 0, insert_luimm12, extract_luimm12, 0 },
/* upper 6 bits of 12 bit unsigned immediate */
#define LUIMM12UP6 (LUIMM12 + 1)
{ 12, 0, 0, 0, insert_luimm12up6, extract_luimm12, 0 },
/* 11 bit pc-relative signed immediate. */
#define LPCREL11 (LUIMM12UP6 + 1)
{ 11, 0, TXVU_OPERAND_SIGNED + TXVU_OPERAND_RELATIVE_BRANCH, 0, 0, 0, 0 },
/* Destination indicator, single letter only, with leading '.' or '/'. */
#define LDOTDEST1 (LPCREL11 + 1)
{ 4, TXVU_SHIFT_DEST, TXVU_OPERAND_SUFFIX,
/* Note that we borrow the insert/extract/print functions from the
vector case. */
parse_dotdest1, insert_dotdest, extract_dotdest, print_dotdest },
/* Destination indicator, single letter only, no leading '.'. */
#define LDEST1 (LDOTDEST1 + 1)
{ 0, 0, 0, parse_dest1, 0, 0, print_dest1 },
/* end of list place holder */
{ 0 }
};
/* Macros to put a field's value into the right place. */
/* ??? If assembler needs these, move to opcode/txvu.h. */
/* value X, B bits, shift S */
#define V(x,b,s) (((x) & ((1 << (b)) - 1)) << (s))
/* Field value macros for both upper and lower instructions.
These shift a value into the right place in the instruction. */
/* [FI] T reg field (remember it's V for value, not vector, here). */
#define VT(x) V ((x), 5, TXVU_SHIFT_TREG)
/* [FI] S reg field. */
#define VS(x) V ((x), 5, TXVU_SHIFT_SREG)
/* [FI] D reg field. */
#define VD(x) V ((x), 5, TXVU_SHIFT_DREG)
/* DEST field. */
#define VDEST(x) V ((x), 4, 21)
/* Masks for fields in both upper and lower instructions.
These mask out all bits but the ones for the field in the instruction. */
#define MT VT (~0)
#define MS VS (~0)
#define MD VD (~0)
#define MDEST VDEST (~0)
/* Upper instruction Value macros. */
/* Upper Flag bits. */
#define VUF(x) V ((x), 5, 27)
/* Upper REServed two bits next to flag bits. */
#define VURES(x) V ((x), 2, 25)
/* 4 bit opcode field. */
#define VUOP4(x) V ((x), 4, 2)
/* 6 bit opcode field. */
#define VUOP6(x) V ((x), 6, 0)
/* 9 bit opcode field. */
#define VUOP9(x) V ((x), 9, 2)
/* 11 bit opcode field. */
#define VUOP11(x) V ((x), 11, 0)
/* BroadCast field. */
#define VUBC(x) V ((x), 2, 0)
/* Upper instruction field masks. */
#define MUUBITS (VUF (~0) + VURES (~0))
#define MURES VURES (~0)
#define MUOP4 VUOP4 (~0)
#define MUOP6 VUOP6 (~0)
#define MUOP9 VUOP9 (~0)
#define MUOP11 VUOP11 (~0)
/* A space, separates instruction name (mnemonic + mnemonic operands) from
operands. */
#define SP ' '
/* Commas separate operands. */
#define C ','
/* Special I,P,Q,R operands. */
#define I 'i'
#define P 'p'
#define Q 'q'
#define R 'r'
/* TXVU instructions.
[??? some of these comments are left over from the ARC port from which
this code is borrowed, delete in time]
Longer versions of insns must appear before shorter ones (if gas sees
"lsr r2,r3,1" when it's parsing "lsr %a,%b" it will think the ",1" is
junk). This isn't necessary for `ld' because of the trailing ']'.
Instructions that are really macros based on other insns must appear
before the real insn so they're chosen when disassembling. Eg: The `mov'
insn is really the `and' insn.
This table is best viewed on a wide screen (161 columns). I'd prefer to
keep it this way. The rest of the file, however, should be viewable on an
80 column terminal. */
/* ??? This table also includes macros: asl, lsl, and mov. The ppc port has
a more general facility for dealing with macros which could be used if
we need to. */
/* These tables can't be `const' because members `next_asm' and `next_dis' are
computed at run-time. We could split this into two, as that would put the
constant stuff into a readonly section. */
struct txvu_opcode txvu_upper_opcodes[] =
{
/* Macros appear first, so the disassembler will try them first. */
/* ??? Any aliases? */
/* ??? When close to being finished, clean up by aligning fields. */
/* The rest of these needn't be sorted, but it helps to find them if they are. */
{ "abs", { DOTDEST, SP, VFTREG, C, VFSREG }, MURES + MUOP11, VUOP11 (0x1fd) },
{ "add", { DOTDEST, SP, VFDREG, C, VFSREG, C, VFTREG }, MURES + MUOP6, VUOP6 (0x28) },
{ "addi", { DOTDEST, SP, VFDREG, C, VFSREG, C, I }, MURES + MT + MUOP6, VUOP6 (0x22) },
{ "addq", { DOTDEST, SP, VFDREG, C, VFSREG, C, Q }, MURES + MT + MUOP6, VUOP6 (0x20) },
{ "add", { UBC, DOTDEST, SP, VFDREG, C, VFSREG, C, UBCFTREG }, MURES + VUOP4 (~0), VUOP4 (0) },
{ "adda", { DOTDEST, SP, UACCDEST, C, VFSREG, C, VFTREG }, MURES + MUOP11, VUOP11 (0x2bc) },
{ "addai", { DOTDEST, SP, UACCDEST, C, VFSREG, C, I }, MURES + MT + MUOP11, VUOP11 (0x23e) },
{ "addaq", { DOTDEST, SP, UACCDEST, C, VFSREG, C, Q }, MURES + MT + MUOP11, VUOP11 (0x23c) },
{ "adda", { UBC, DOTDEST, SP, UACCDEST, C, VFSREG, C, UBCFTREG }, MURES + MUOP9, VUOP9 (0xf) },
{ "clip", { DOTDEST, SP, VFSREG }, MURES + MDEST + MT + MUOP11, VDEST (0xf) + VUOP11 (0x1ff) },
{ "ftoi0", { DOTDEST, SP, VFTREG, C, VFSREG }, MURES + MUOP11, VUOP11 (0x17c) },
{ "ftoi4", { DOTDEST, SP, VFTREG, C, VFSREG }, MURES + MUOP11, VUOP11 (0x17d) },
{ "ftoi12", { DOTDEST, SP, VFTREG, C, VFSREG }, MURES + MUOP11, VUOP11 (0x17e) },
{ "ftoi15", { DOTDEST, SP, VFTREG, C, VFSREG }, MURES + MUOP11, VUOP11 (0x17f) },
{ "itof0", { DOTDEST, SP, VFTREG, C, VFSREG }, MURES + MUOP11, VUOP11 (0x13c) },
{ "itof4", { DOTDEST, SP, VFTREG, C, VFSREG }, MURES + MUOP11, VUOP11 (0x13d) },
{ "itof12", { DOTDEST, SP, VFTREG, C, VFSREG }, MURES + MUOP11, VUOP11 (0x13e) },
{ "itof15", { DOTDEST, SP, VFTREG, C, VFSREG }, MURES + MUOP11, VUOP11 (0x13f) },
{ "madd", { DOTDEST, SP, VFDREG, C, VFSREG, C, VFTREG }, MURES + MUOP6, VUOP6 (0x29) },
{ "maddi", { DOTDEST, SP, VFDREG, C, VFSREG, C, I }, MURES + MT + MUOP6, VUOP6 (0x23) },
{ "maddq", { DOTDEST, SP, VFDREG, C, VFSREG, C, Q }, MURES + MT + MUOP6, VUOP6 (0x21) },
{ "madd", { UBC, DOTDEST, SP, VFDREG, C, VFSREG, C, UBCFTREG }, MURES + MUOP4, VUOP4 (0x2) },
{ "madda", { DOTDEST, SP, UACCDEST, C, VFSREG, C, VFTREG }, MURES + MUOP11, VUOP11 (0x2bd) },
{ "maddai", { DOTDEST, SP, UACCDEST, C, VFSREG, C, I }, MURES + MT + MUOP11, VUOP11 (0x23f) },
{ "maddaq", { DOTDEST, SP, UACCDEST, C, VFSREG, C, Q }, MURES + MT + MUOP11, VUOP11 (0x23d) },
{ "madda", { UBC, DOTDEST, SP, UACCDEST, C, VFSREG, C, UBCFTREG }, MURES + MUOP9, VUOP9 (0x2f) },
{ "max", { DOTDEST, SP, VFDREG, C, VFSREG, C, VFTREG }, MURES + MUOP6, VUOP6 (0x2b) },
{ "maxi", { DOTDEST, SP, VFDREG, C, VFSREG, C, I }, MURES + MT + MUOP6, VUOP6 (0x2d) },
{ "max", { UBC, DOTDEST, SP, VFDREG, C, VFSREG, C, UBCFTREG }, MURES + MUOP4, VUOP4 (0x4) },
/* ??? mini or min? */
{ "mini", { DOTDEST, SP, VFDREG, C, VFSREG, C, VFTREG }, MURES + MUOP6, VUOP6 (0x2f) },
{ "minii", { DOTDEST, SP, VFDREG, C, VFSREG, C, I }, MURES + MT + MUOP6, VUOP6 (0x1f) },
{ "mini", { UBC, DOTDEST, SP, VFDREG, C, VFSREG, C, UBCFTREG }, MURES + MUOP4, VUOP4 (0x5) },
{ "msub", { DOTDEST, SP, VFDREG, C, VFSREG, C, VFTREG }, MURES + MUOP6, VUOP6 (0x2d) },
{ "msubi", { DOTDEST, SP, VFDREG, C, VFSREG, C, I }, MURES + MT + MUOP6, VUOP6 (0x27) },
{ "msubq", { DOTDEST, SP, VFDREG, C, VFSREG, C, Q }, MURES + MT + MUOP6, VUOP6 (0x25) },
{ "msub", { UBC, DOTDEST, SP, VFDREG, C, VFSREG, C, UBCFTREG }, MURES + MUOP4, VUOP4 (0x3) },
{ "msuba", { DOTDEST, SP, UACCDEST, C, VFSREG, C, VFTREG }, MURES + MUOP11, VUOP11 (0x2fd) },
{ "msubai", { DOTDEST, SP, UACCDEST, C, VFSREG, C, I }, MURES + MT + MUOP11, VUOP11 (0x27f) },
{ "msubaq", { DOTDEST, SP, UACCDEST, C, VFSREG, C, Q }, MURES + MT + MUOP11, VUOP11 (0x27d) },
{ "msuba", { UBC, DOTDEST, SP, UACCDEST, C, VFSREG, C, UBCFTREG }, MURES + MUOP9, VUOP9 (0x3f) },
{ "mul", { DOTDEST, SP, VFDREG, C, VFSREG, C, VFTREG }, MURES + MUOP6, VUOP6 (0x2a) },
{ "muli", { DOTDEST, SP, VFDREG, C, VFSREG, C, I }, MURES + MT + MUOP6, VUOP6 (0x1e) },
{ "mulq", { DOTDEST, SP, VFDREG, C, VFSREG, C, Q }, MURES + MT + MUOP6, VUOP6 (0x1c) },
{ "mul", { UBC, DOTDEST, SP, VFDREG, C, VFSREG, C, UBCFTREG }, MURES + VUOP4 (~0), VUOP4 (6) },
{ "mula", { DOTDEST, SP, UACCDEST, C, VFSREG, C, VFTREG }, MURES + MUOP11, VUOP11 (0x2be) },
{ "mulai", { DOTDEST, SP, UACCDEST, C, VFSREG, C, I }, MURES + MT + MUOP11, VUOP11 (0x1fe) },
{ "mulaq", { DOTDEST, SP, UACCDEST, C, VFSREG, C, Q }, MURES + MT + MUOP11, VUOP11 (0x1fc) },
{ "mula", { UBC, DOTDEST, SP, UACCDEST, C, VFSREG, C, UBCFTREG }, MURES + MUOP9, VUOP9 (0x6f) },
{ "nop", { 0 }, MURES + MDEST + MT + MS + MUOP11, VUOP11 (0x2ff) },
{ "opmula", { DOTDEST, SP, UACCDEST, C, VFSREG, C, VFTREG, UXYZ }, MURES + MUOP11, VUOP11 (0x2fe) },
{ "opmsub", { DOTDEST, SP, VFDREG, C, VFSREG, C, VFTREG, UXYZ }, MURES + MUOP6, VUOP6 (0x2e) },
{ "sub", { DOTDEST, SP, VFDREG, C, VFSREG, C, VFTREG }, MURES + MUOP6, VUOP6 (0x2c) },
{ "subi", { DOTDEST, SP, VFDREG, C, VFSREG, C, I }, MURES + MT + MUOP6, VUOP6 (0x26) },
{ "subq", { DOTDEST, SP, VFDREG, C, VFSREG, C, Q }, MURES + MT + MUOP6, VUOP6 (0x24) },
{ "sub", { UBC, DOTDEST, SP, VFDREG, C, VFSREG, C, UBCFTREG }, MURES + VUOP4 (~0), VUOP4 (1) },
{ "suba", { DOTDEST, SP, UACCDEST, C, VFSREG, C, VFTREG }, MURES + MUOP11, VUOP11 (0x2fc) },
{ "subai", { DOTDEST, SP, UACCDEST, C, VFSREG, C, I }, MURES + MT + MUOP11, VUOP11 (0x27e) },
{ "subaq", { DOTDEST, SP, UACCDEST, C, VFSREG, C, Q }, MURES + MT + MUOP11, VUOP11 (0x27c) },
{ "suba", { UBC, DOTDEST, SP, UACCDEST, C, VFSREG, C, UBCFTREG }, MURES + MUOP9, VUOP9 (0x1f) }
};
const int txvu_upper_opcodes_count = sizeof (txvu_upper_opcodes) / sizeof (txvu_upper_opcodes[0]);
/* Lower instruction Value macros. */
/* 6 bit opcode. */
#define VLOP6(x) V ((x), 6, 0)
/* 7 bit opcode. */
#define VLOP7(x) V ((x), 7, 25)
/* 11 bit opcode. */
#define VLOP11(x) V ((x), 11, 0)
/* 11 bit immediate. */
#define VLIMM11(x) V ((x), 11, 0)
/* FTF field. */
#define VLFTF(x) V ((x), 2, 23)
/* FSF field. */
#define VLFSF(x) V ((x), 2, 21)
/* Upper bit of 12 bit unsigned immediate. */
#define VLUIMM12TOP(x) V ((x), 1, 21)
/* Upper 4 bits of 15 bit unsigned immediate. */
#define VLUIMM15TOP(x) VDEST (x)
/* Lower instruction field masks. */
#define MLOP6 VLOP6 (~0)
#define MLOP7 VLOP7 (~0)
#define MLOP11 VLOP11 (~0)
#define MLIMM11 VLIMM11 (~0)
#define MLB24 V (1, 1, 24)
#define MLUIMM12TOP VLUIMM12TOP (~0)
/* 12 bit unsigned immediates are split into two parts, 1 bit and 11 bits.
The upper 1 bit is part of the `dest' field. This mask is for the
other 3 bits of the dest field. */
#define MLUIMM12UNUSED V (7, 3, 22)
#define MLUIMM15TOP MDEST
struct txvu_opcode txvu_lower_opcodes[] =
{
/* Macros appear first, so the disassembler will try them first. */
/* ??? Any aliases? */
/* ??? There isn't an explicit nop. Apparently it's "move vf0,vf0". */
{ "nop", { 0 }, 0xffffffff, VLOP7 (0x40) + VLIMM11 (0x33c) },
/* The rest of these needn't be sorted, but it helps to find them if they are. */
{ "b", { SP, LPCREL11 }, MLOP7 + MDEST + MT + MS, VLOP7 (0x20) },
{ "bal", { SP, LITREG, C, LPCREL11 }, MLOP7 + MDEST + MS, VLOP7 (0x21) },
{ "div", { SP, Q, C, LFSFFSREG, C, LFTFFTREG }, MLOP7 + MLOP11, VLOP7 (0x40) + VLOP11 (0x3bc) },
{ "eatan", { SP, P, C, LFSFFSREG }, MLOP7 + VLFTF (~0) + MT + MLOP11, VLOP7 (0x40) + VLOP11 (0x7fd) },
{ "eatanxy", { SP, P, C, LFSREG }, MLOP7 + MDEST + MT + MLOP11, VLOP7 (0x40) + VDEST (0xf) + VLOP11 (0x77c) },
{ "eatanxz", { SP, P, C, LFSREG }, MLOP7 + MDEST + MT + MLOP11, VLOP7 (0x40) + VDEST (0xf) + VLOP11 (0x77d) },
{ "eexp", { SP, P, C, LFSFFSREG }, MLOP7 + VLFTF (~0) + MT + MLOP11, VLOP7 (0x40) + VLOP11 (0x7fe) },
{ "eleng", { SP, P, C, LFSREG }, MLOP7 + MDEST + MT + MLOP11, VLOP7 (0x40) + VDEST (0xf) + VLOP11 (0x74e) },
{ "ercpr", { SP, P, C, LFSFFSREG }, MLOP7 + VLFTF (~0) + MT + MLOP11, VLOP7 (0x40) + VLOP11 (0x7be) },
{ "erleng", { SP, P, C, LFSREG }, MLOP7 + MDEST + MT + MLOP11, VLOP7 (0x40) + VDEST (0xf) + VLOP11 (0x73f) },
{ "ersadd", { SP, P, C, LFSREG }, MLOP7 + MDEST + MT + MLOP11, VLOP7 (0x40) + VDEST (0xf) + VLOP11 (0x73d) },
{ "ersqrt", { SP, P, C, LFSFFSREG }, MLOP7 + VLFTF (~0) + MT + MLOP11, VLOP7 (0x40) + VLOP11 (0x7bd) },
{ "esadd", { SP, P, C, LFSREG }, MLOP7 + MDEST + MT + MLOP11, VLOP7 (0x40) + VDEST (0xf) + VLOP11 (0x73c) },
{ "esin", { SP, P, C, LFSFFSREG }, MLOP7 + VLFTF (~0) + MT + MLOP11, VLOP7 (0x40) + VLOP11 (0x7fc) },
{ "esqrt", { SP, P, C, LFSFFSREG }, MLOP7 + VLFTF (~0) + MT + MLOP11, VLOP7 (0x40) + VLOP11 (0x7bc) },
{ "esum", { SP, P, C, LFSREG }, MLOP7 + MDEST + MT + MLOP11, VLOP7 (0x40) + VDEST (0xf) + VLOP11 (0x77e) },
{ "fcand", { SP, LVI01, C, LUIMM24 }, MLOP7 + MLB24, VLOP7 (0x12) },
{ "fceq", { SP, LVI01, C, LUIMM24 }, MLOP7 + MLB24, VLOP7 (0x10) },
{ "fcget", { SP, LITREG }, MLOP7 + MDEST + MS + MLIMM11, VLOP7 (0x1c) },
{ "fcor", { SP, LVI01, C, LUIMM24 }, MLOP7 + MLB24, VLOP7 (0x13) },
{ "fcset", { SP, LUIMM24 }, MLOP7 + MLB24, VLOP7 (0x11) },
{ "fmand", { SP, LITREG, C, LISREG }, MLOP7 + MDEST + MLIMM11, VLOP7 (0x1a) },
{ "fmeq", { SP, LITREG, C, LISREG }, MLOP7 + MDEST + MLIMM11, VLOP7 (0x18) },
{ "fmor", { SP, LITREG, C, LISREG }, MLOP7 + MDEST + MLIMM11, VLOP7 (0x1b) },
{ "fsand", { SP, LITREG, C, LUIMM12 }, MLOP7 + MLUIMM12UNUSED + MS, VLOP7 (0x16) },
{ "fseq", { SP, LITREG, C, LUIMM12 }, MLOP7 + MLUIMM12UNUSED + MS, VLOP7 (0x14) },
{ "fsor", { SP, LITREG, C, LUIMM12 }, MLOP7 + MLUIMM12UNUSED + MS, VLOP7 (0x17) },
{ "fsset", { SP, LUIMM12UP6 }, MLOP7 + MLUIMM12UNUSED + V (~0, 6, 0) + MS + MT, VLOP7 (0x15) },
{ "iadd", { SP, LIDREG, C, LISREG, C, LITREG }, MLOP7 + MDEST + MLOP6, VLOP7 (0x40) + VLOP6 (0x30) },
{ "iaddi", { SP, LITREG, C, LISREG, C, LIMM5 }, MLOP7 + MDEST + MLOP6, VLOP7 (0x40) + VLOP6 (0x32) },
{ "iaddiu", { SP, LITREG, C, LISREG, C, LUIMM15 }, MLOP7, VLOP7 (0x08) },
{ "iand", { SP, LIDREG, C, LISREG, C, LITREG }, MLOP7 + MDEST + MLOP6, VLOP7 (0x40) + VLOP6 (0x34) },
{ "ibeq", { SP, LITREG, C, LISREG, C, LPCREL11 }, MLOP7 + MDEST, VLOP7 (0x28) },
{ "ibgez", { SP, LISREG, C, LPCREL11 }, MLOP7 + MDEST + MT, VLOP7 (0x2f) },
{ "ibgtz", { SP, LISREG, C, LPCREL11 }, MLOP7 + MDEST + MT, VLOP7 (0x2d) },
{ "iblez", { SP, LISREG, C, LPCREL11 }, MLOP7 + MDEST + MT, VLOP7 (0x2e) },
{ "ibltz", { SP, LISREG, C, LPCREL11 }, MLOP7 + MDEST + MT, VLOP7 (0x2c) },
{ "ibne", { SP, LITREG, C, LISREG, C, LPCREL11 }, MLOP7 + MDEST, VLOP7 (0x29) },
{ "ilw", { LDOTDEST1, SP, LITREG, C, LIMM11, '(', LISREG, ')', LDEST1 }, MLOP7, VLOP7 (0x04) },
{ "ilwr", { LDOTDEST1, SP, LITREG, C, '(', LISREG, ')', LDEST1 }, MLOP7 + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x3fe) },
{ "ior", { SP, LIDREG, C, LISREG, C, LITREG }, MLOP7 + MDEST + MLOP6, VLOP7 (0x40) + VLOP6 (0x35) },
{ "isub", { SP, LIDREG, C, LISREG, C, LITREG }, MLOP7 + MDEST + MLOP6, VLOP7 (0x40) + VLOP6 (0x31) },
{ "isubiu", { SP, LITREG, C, LISREG, C, LUIMM15 }, MLOP7, VLOP7 (0x09) },
{ "isw", { LDOTDEST1, SP, LITREG, C, LIMM11, '(', LISREG, ')', LDEST1 }, MLOP7, VLOP7 (0x05) },
{ "iswr", { LDOTDEST1, SP, LITREG, C, '(', LISREG, ')', LDEST1 }, MLOP7 + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x3ff) },
{ "jalr", { SP, LITREG, C, LISREG }, MLOP7 + MDEST + MLIMM11, VLOP7 (0x25) },
{ "jr", { SP, LISREG }, MLOP7 + MDEST + MT + MLIMM11, VLOP7 (0x24) },
{ "lq", { DOTDEST, SP, VFTREG, C, LIMM11, '(', LISREG, ')' }, MLOP7, VLOP7 (0x00) },
{ "lqd", { DOTDEST, SP, VFTREG, C, '(', '-', '-', LISREG, ')' }, MLOP7 + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x37e) },
{ "lqi", { DOTDEST, SP, VFTREG, C, '(', LISREG, '+', '+', ')' }, MLOP7 + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x37c) },
/* Only a single VF reg is allowed here. We can use VFTREG because LDOTDEST1
handles verifying only a single choice of xyzw is present. */
{ "mfir", { LDOTDEST1, SP, VFTREG, C, LISREG }, MLOP7 + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x3fc) },
{ "mfp", { DOTDEST, SP, VFTREG, C, P }, MLOP7 + MS + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x67c) },
{ "move", { DOTDEST, SP, VFTREG, C, VFSREG }, MLOP7 + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x33c) },
{ "mr32", { DOTDEST, SP, VFTREG, C, VFSREG }, MLOP7 + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x33d) },
{ "mtir", { LDOTDEST1, SP, LITREG, C, VFSREG }, MLOP7 + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x3fd) },
{ "rget", { DOTDEST, SP, VFTREG, C, R }, MLOP7 + MS + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x43d) },
{ "rinit", { SP, R, C, LFSFFSREG }, MLOP7 + VLFTF (~0) + MT + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x43e) },
{ "rnext", { DOTDEST, SP, VFTREG, C, R }, MLOP7 + MS + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x43c) },
{ "rsqrt", { SP, Q, C, LFSFFSREG, C, LFTFFTREG }, MLOP7 + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x3be) },
{ "rxor", { SP, R, C, LFSFFSREG }, MLOP7 + VLFTF (~0) + MT + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x43f) },
{ "sq", { DOTDEST, SP, VFTREG, C, LIMM11, '(', LISREG, ')' }, MLOP7, VLOP7 (0x01) },
{ "sqd", { DOTDEST, SP, VFTREG, C, '(', '-', '-', LISREG, ')' }, MLOP7 + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x37f) },
{ "sqi", { DOTDEST, SP, VFTREG, C, '(', LISREG, '+', '+', ')' }, MLOP7 + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x37d) },
{ "sqrt", { SP, Q, C, LFTFFTREG }, MLOP7 + VLFSF (~0) + MS + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x3bd) },
{ "waitp", { 0 }, 0xffffffff, VLOP7 (0x40) + VLIMM11 (0x7bf) },
{ "waitq", { 0 }, 0xffffffff, VLOP7 (0x40) + VLIMM11 (0x3bf) },
{ "xgkick", { SP, LISREG }, MLOP7 + MDEST + MT + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x6fc) },
{ "xitop", { SP, LITREG }, MLOP7 + MDEST + MS + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x6bd) },
{ "xtop", { SP, LITREG }, MLOP7 + MDEST + MS + MLIMM11, VLOP7 (0x40) + VLIMM11 (0x6bc) }
};
const int txvu_lower_opcodes_count = sizeof (txvu_lower_opcodes) / sizeof (txvu_lower_opcodes[0]);
/* Value of DEST in use.
Each of the registers must specify the same value as the opcode.
??? Perhaps remove the duplication? */
static int state_vu_mnemonic_dest;
/* Value of BC to use.
The register specified for the ftreg must match the broadcast register
specified in the opcode. */
static int state_vu_mnemonic_bc;
/* Multiple destination choice support.
The "dest" string selects any combination of x,y,z,w.
[The letters are ordered that way to follow the manual's style.] */
/* Utility to parse a `dest' spec.
Return the found value.
*PSTR is set to the character that terminated the parsing.
It is up to the caller to do any error checking. */
static long
u_parse_dest (pstr)
char **pstr;
{
long dest = 0;
while (**pstr)
{
switch (**pstr)
{
case 'x' : case 'X' : dest |= TXVU_DEST_X; break;
case 'y' : case 'Y' : dest |= TXVU_DEST_Y; break;
case 'z' : case 'Z' : dest |= TXVU_DEST_Z; break;
case 'w' : case 'W' : dest |= TXVU_DEST_W; break;
default : return dest;
}
++*pstr;
}
return dest;
}
static long
parse_dotdest (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
long dest;
if (**pstr != '.' && **pstr != '/')
{
*errmsg = "missing `.'";
return 0;
}
++*pstr;
dest = u_parse_dest (pstr);
if (dest == 0 || isalnum (**pstr))
{
*errmsg = "invalid `dest'";
return 0;
}
return dest;
}
/* Parse a `dest' spec where only a single letter is allowed,
but the encoding handles all four. */
static long
parse_dotdest1 (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char c;
long dest;
if (**pstr != '.' && **pstr != '/')
{
*errmsg = "missing `.'";
return 0;
}
++*pstr;
switch (**pstr)
{
case 'x' : case 'X' : dest = TXVU_DEST_X; break;
case 'y' : case 'Y' : dest = TXVU_DEST_Y; break;
case 'z' : case 'Z' : dest = TXVU_DEST_Z; break;
case 'w' : case 'W' : dest = TXVU_DEST_W; break;
default : *errmsg = "invalid `dest'"; return 0;
}
++*pstr;
c = tolower (**pstr);
if (c == 'x' || c == 'y' || c == 'z' || c == 'w')
{
*errmsg = "only one of x,y,z,w can be specified";
return 0;
}
if (isalnum (**pstr))
{
*errmsg = "invalid `dest'";
return 0;
}
return dest;
}
/* Parse a `dest' spec with no leading '.', where only a single letter is
allowed, but the encoding handles all four. The parsed value must match
that recorded in `dest'. */
static long
parse_dest1 (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char c;
long dest;
dest = u_parse_dest (pstr);
if (dest != TXVU_DEST_X
&& dest != TXVU_DEST_Y
&& dest != TXVU_DEST_Z
&& dest != TXVU_DEST_W)
{
*errmsg = "expecting one of x,y,z,w";
return 0;
}
if (dest != state_vu_mnemonic_dest)
{
*errmsg = "`dest' suffix does not match instruction `dest'";
return 0;
}
return dest;
}
static void
insert_dotdest (opcode, operand, mods, insn, value, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
long value;
const char **errmsg;
{
/* Record the DEST value in use so the register parser can use it. */
state_vu_mnemonic_dest = value;
*insn |= value << operand->shift;
}
static long
extract_dotdest (opcode, operand, mods, insn, pinvalid)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
int *pinvalid;
{
/* Record the DEST value in use so the register printer can use it. */
state_vu_mnemonic_dest = (*insn >> operand->shift) & ((1 << operand->bits) - 1);
return state_vu_mnemonic_dest;
}
/* Utility to print a multiple dest spec. */
static void
u_print_dest (info, insn, value)
disassemble_info *info;
TXVU_INSN *insn;
long value;
{
if (value & TXVU_DEST_X)
(*info->fprintf_func) (info->stream, "x");
if (value & TXVU_DEST_Y)
(*info->fprintf_func) (info->stream, "y");
if (value & TXVU_DEST_Z)
(*info->fprintf_func) (info->stream, "z");
if (value & TXVU_DEST_W)
(*info->fprintf_func) (info->stream, "w");
}
static void
print_dotdest (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
(*info->fprintf_func) (info->stream, ".");
u_print_dest (info, insn, value);
}
static void
print_dest1 (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
u_print_dest (info, insn, state_vu_mnemonic_dest);
}
/* Utilities for single destination choice handling. */
static long
u_parse_sdest (pstr, errmsg)
char **pstr;
const char **errmsg;
{
char c;
long dest = 0;
switch (**pstr)
{
case 'x' : case 'X' : dest = TXVU_SDEST_X; break;
case 'y' : case 'Y' : dest = TXVU_SDEST_Y; break;
case 'z' : case 'Z' : dest = TXVU_SDEST_Z; break;
case 'w' : case 'W' : dest = TXVU_SDEST_W; break;
default : *errmsg = "only one of x,y,z,w can be specified"; return 0;
}
++*pstr;
c = tolower (**pstr);
if (c == 'x' || c == 'y' || c == 'z' || c == 'w')
{
*errmsg = "only one of x,y,z,w can be specified";
return 0;
}
if (isalnum (**pstr))
{
*errmsg = "invalid `dest'";
return 0;
}
return dest;
}
static void
print_sdest (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
char c;
switch (value)
{
case TXVU_SDEST_X : c = 'x'; break;
case TXVU_SDEST_Y : c = 'y'; break;
case TXVU_SDEST_Z : c = 'z'; break;
case TXVU_SDEST_W : c = 'w'; break;
}
(*info->fprintf_func) (info->stream, "%c", c);
}
/* Broadcase field. */
static long
parse_bc (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
long value = u_parse_sdest (pstr, errmsg);
if (*errmsg)
return 0;
/* Save value for later verification in register parsing. */
state_vu_mnemonic_bc = value;
return value;
}
/* During the extraction process, save the bc field for use in
printing the bc register. */
static long
extract_bc (opcode, operand, mods, insn, pinvalid)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
int *pinvalid;
{
state_vu_mnemonic_bc = *insn & 3;
return state_vu_mnemonic_bc;
}
static long
parse_vfreg (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
char *start;
long reg;
int reg_dest;
if (tolower (str[0]) != 'v'
|| tolower (str[1]) != 'f')
{
*errmsg = "unknown register";
return 0;
}
/* FIXME: quick hack until the framework works. */
start = str = str + 2;
while (*str && isdigit (*str))
++str;
reg = atoi (start);
if (reg < 0 || reg > 31)
{
*errmsg = "invalid register number";
return 0;
}
reg_dest = u_parse_dest (&str);
if (reg_dest == 0 || isalnum (*str))
{
*errmsg = "invalid `dest'";
return 0;
}
if (reg_dest != state_vu_mnemonic_dest)
{
*errmsg = "register `dest' does not match instruction `dest'";
return 0;
}
*pstr = str;
return reg;
}
static void
print_vfreg (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
disassemble_info *info;
TXVU_INSN *insn;
long value;
{
(*info->fprintf_func) (info->stream, "vf%02ld", value);
u_print_dest (info, insn, state_vu_mnemonic_dest);
}
/* FT register in broadcast case. */
static long
parse_bcftreg (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
char *start;
long reg;
int reg_bc;
if (tolower (str[0]) != 'v'
|| tolower (str[1]) != 'f')
{
*errmsg = "unknown register";
return 0;
}
/* FIXME: quick hack until the framework works. */
start = str = str + 2;
while (*str && isdigit (*str))
++str;
reg = atoi (start);
if (reg < 0 || reg > 31)
{
*errmsg = "invalid register number";
return 0;
}
reg_bc = u_parse_sdest (&str, errmsg);
if (*errmsg)
return 0;
if (reg_bc != state_vu_mnemonic_bc)
{
*errmsg = "register `bc' does not match instruction `bc'";
return 0;
}
*pstr = str;
return reg;
}
static void
print_bcftreg (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
(*info->fprintf_func) (info->stream, "vf%02ld", value);
print_sdest (opcode, operand, mods, insn, info, state_vu_mnemonic_bc);
}
/* ACC handling. */
static long
parse_accdest (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
long acc_dest = 0;
if (strncasecmp (str, "acc", 3) != 0)
{
*errmsg = "expecting `acc'";
return 0;
}
str += 3;
acc_dest = u_parse_dest (&str);
if (acc_dest == 0 || isalnum (*str))
{
*errmsg = "invalid `dest'";
return 0;
}
if (acc_dest != state_vu_mnemonic_dest)
{
*errmsg = "acc `dest' does not match instruction `dest'";
return 0;
}
*pstr = str;
/* Value isn't used, but we must return something. */
return 0;
}
static void
print_accdest (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
disassemble_info *info;
TXVU_INSN *insn;
long value;
{
(*info->fprintf_func) (info->stream, "acc");
u_print_dest (info, insn, state_vu_mnemonic_dest);
}
/* XYZ operand handling.
This simplifies the opmula,opmsub entries by keeping them equivalent to
the others. */
static void
insert_xyz (opcode, operand, mods, insn, value, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
long value;
const char **errmsg;
{
if (state_vu_mnemonic_dest != (TXVU_DEST_X | TXVU_DEST_Y | TXVU_DEST_Z))
*errmsg = "expecting `xyz' for `dest' value";
}
/* F[ST] register using selector in F[ST]F field.
Internally, the value is encoded in 7 bits: the 2 bit xyzw indicator
followed by the 5 bit register number. */
static long
parse_ffstreg (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
char *start;
int reg, xyzw;
if (tolower (str[0]) != 'v'
|| tolower (str[1]) != 'f')
{
*errmsg = "unknown register";
return 0;
}
/* FIXME: quick hack until the framework works. */
start = str = str + 2;
while (*str && isdigit (*str))
++str;
reg = atoi (start);
if (reg < 0 || reg > 31)
{
*errmsg = "invalid register number";
return 0;
}
xyzw = u_parse_sdest (&str, errmsg);
if (*errmsg)
return 0;
*pstr = str;
return reg | (xyzw << 5);
}
static void
print_ffstreg (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
(*info->fprintf_func) (info->stream, "vf%02ld", value & TXVU_MASK_REG);
print_sdest (opcode, operand, mods, insn, info, (value >> 5) & 3);
}
static void
insert_ffstreg (opcode, operand, mods, insn, value, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
long value;
const char **errmsg;
{
if (operand->shift == TXVU_SHIFT_SREG)
*insn |= VLFSF (value >> 5) | VS (value);
else
*insn |= VLFTF (value >> 5) | VT (value);
}
static long
extract_ffstreg (opcode, operand, mods, insn, pinvalid)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
int *pinvalid;
{
if (operand->shift == TXVU_SHIFT_SREG)
return (((*insn & VLFSF (~0)) >> 21) << 5) | VS (*insn);
else
return (((*insn & VLFTF (~0)) >> 21) << 5) | VT (*insn);
}
/* F register. */
static long
parse_freg (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
char *start;
long reg;
if (tolower (str[0]) != 'v'
|| tolower (str[1]) != 'f')
{
*errmsg = "unknown register";
return 0;
}
/* FIXME: quick hack until the framework works. */
start = str = str + 2;
while (*str && isdigit (*str))
++str;
reg = atoi (start);
if (reg < 0 || reg > 31)
{
*errmsg = "invalid register number";
return 0;
}
*pstr = str;
return reg;
}
static void
print_freg (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
(*info->fprintf_func) (info->stream, "vf%02ld", value);
}
/* I register. */
static long
parse_ireg (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
char *start;
long reg;
if (tolower (str[0]) != 'v'
|| tolower (str[1]) != 'i')
{
*errmsg = "unknown register";
return 0;
}
/* FIXME: quick hack until the framework works. */
start = str = str + 2;
while (*str && isdigit (*str))
++str;
reg = atoi (start);
if (reg < 0 || reg > 31)
{
*errmsg = "invalid register number";
return 0;
}
*pstr = str;
return reg;
}
static void
print_ireg (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
(*info->fprintf_func) (info->stream, "vi%02ld", value);
}
/* VI01 register. */
static long
parse_vi01 (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
char *start;
long reg;
if (tolower (str[0]) != 'v'
|| tolower (str[1]) != 'i')
{
*errmsg = "unknown register";
return 0;
}
/* FIXME: quick hack until the framework works. */
start = str = str + 2;
while (*str && isdigit (*str))
++str;
reg = atoi (start);
if (reg != 1)
{
*errmsg = "vi01 required here";
return 0;
}
*pstr = str;
return reg;
}
static void
print_vi01 (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
(*info->fprintf_func) (info->stream, "vi01");
}
/* Lower instruction 12 bit unsigned immediate. */
static void
insert_luimm12 (opcode, operand, mods, insn, value, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
long value;
const char **errmsg;
{
*insn |= VLUIMM12TOP ((value & (1 << 11)) != 0) | VLIMM11 (value);
}
static long
extract_luimm12 (opcode, operand, mods, insn, pinvalid)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
int *pinvalid;
{
return (((*insn & MLUIMM12TOP) != 0) << 11) | VLIMM11 (*insn);
}
/* Lower instruction 12 bit unsigned immediate, upper 6 bits. */
static void
insert_luimm12up6 (opcode, operand, mods, insn, value, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
long value;
const char **errmsg;
{
*insn |= VLUIMM12TOP ((value & (1 << 11)) != 0) | (value & 0x7c0);
}
/* Lower instruction 15 bit unsigned immediate. */
static void
insert_luimm15 (opcode, operand, mods, insn, value, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
long value;
const char **errmsg;
{
*insn |= VLUIMM15TOP (value >> 11) | VLIMM11 (value);
}
static long
extract_luimm15 (opcode, operand, mods, insn, pinvalid)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
int *pinvalid;
{
return (((*insn & MLUIMM15TOP) >> 21) << 11) | VLIMM11 (*insn);
}
/* PKE support. */
PARSE_FN (pke_ibit);
PRINT_FN (pke_ibit);
PARSE_FN (pke_mode);
PRINT_FN (pke_mode);
PARSE_FN (pke_ability);
PRINT_FN (pke_ability);
PARSE_FN (pke_mpgaddr);
PARSE_FN (pke_varlendata);
PARSE_FN (pke_imrbits);
PRINT_FN (pke_imrbits);
PARSE_FN (pke_unpacktype);
PRINT_FN (pke_unpacktype);
PARSE_FN (pke_unpackaddr);
const txvu_operand pke_operands[] =
{
/* place holder (??? not sure if needed) */
#define PKE_UNUSED 128
{ 0 },
/* The I bit. */
#define PKE_IBIT (PKE_UNUSED + 1)
{ 1, 31, TXVU_OPERAND_SUFFIX, parse_pke_ibit, 0, 0, print_pke_ibit },
/* An 8 bit unsigned immediate, stored in upper 8 bits of immed field. */
#define PKE_UIMM8UP (PKE_IBIT + 1)
{ 8, 8, 0, 0, 0, 0, 0 },
/* An 8 bit unsigned immediate, stored in lower 8 bits of immed field. */
#define PKE_UIMM8LO (PKE_UIMM8UP + 1)
{ 8, 0, 0, 0, 0, 0, 0 },
/* An 16 bit unsigned immediate, stored in lower 8 bits of immed field. */
#define PKE_UIMM16 (PKE_UIMM8LO + 1)
{ 16, 0, 0, 0, 0, 0, 0 },
/* The mode operand of `stmod'. */
#define PKE_MODE (PKE_UIMM16 + 1)
{ 2, 0, 0, parse_pke_mode, 0, 0, print_pke_mode },
/* The ability operand of `mskpath3'. */
#define PKE_ABILITY (PKE_MODE + 1)
{ 1, 15, 0, parse_pke_ability, 0, 0, print_pke_ability },
/* A VU address. */
#define PKE_VUADDR (PKE_ABILITY + 1)
{ 16, 0, 0, 0, 0, 0, 0 },
/* A 32 bit immediate, appearing in 2nd,3rd,4th,5th words. */
#define PKE_UIMM32 (PKE_VUADDR + 1)
{ 32, 0, 0, 0, 0, 0, 0 },
/* VU address used by mpg insn. */
#define PKE_MPGADDR (PKE_UIMM32 + 1)
{ 16, 0, TXVU_OPERAND_ADDRESS, parse_pke_mpgaddr, 0, 0, 0 },
/* A variable length data specifier.
Any of: file name, number, or '*'. */
#define PKE_VARLENDATA (PKE_MPGADDR + 1)
{ 0, 0, 0, parse_pke_varlendata, 0, 0, 0 },
/* The IMR bits of the unpack insn. */
#define PKE_IMRBITS (PKE_VARLENDATA + 1)
{ 0, 0, TXVU_OPERAND_SUFFIX, parse_pke_imrbits, 0, 0, print_pke_imrbits },
/* The type of the unpack insn. */
#define PKE_UNPACKTYPE (PKE_IMRBITS + 1)
{ 4, 24, 0, parse_pke_unpacktype, 0, 0, print_pke_unpacktype },
/* VU address used by unpack insn. */
#define PKE_UNPACKADDR (PKE_UIMM32 + 1)
{ 16, 0, TXVU_OPERAND_ADDRESS, parse_pke_unpackaddr, 0, 0, 0 },
/* Fake operand processed at end of parsing an insn to set the length. */
/* end of list place holder */
{ 0 }
};
/* Field mask values. */
#define MPKECMD 0x7f000000
#define MPKEUNPACK 0x60000000
/* Field values. */
#define VPKECMD(x) V ((x), 7, 24)
#define VPKEUNPACK V (0x60, 8, 24)
struct txvu_opcode pke_opcodes[] =
{
{ "pkenop", { PKE_IBIT }, 0x7fffffff, 0 },
{ "stcycle", { PKE_IBIT, SP, PKE_UIMM8UP, C, PKE_UIMM8LO }, MPKECMD, VPKECMD (1) },
{ "offset", { PKE_IBIT, SP, PKE_UIMM16 }, MPKECMD, VPKECMD (2) },
{ "base", { PKE_IBIT, SP, PKE_UIMM16 }, MPKECMD, VPKECMD (3) },
{ "itop", { PKE_IBIT, SP, PKE_UIMM16 }, MPKECMD, VPKECMD (4) },
{ "stmod", { PKE_IBIT, SP, PKE_MODE }, MPKECMD + V (~0, 14, 2), VPKECMD (5) },
{ "mskpath3", { PKE_IBIT, SP, PKE_ABILITY }, MPKECMD + V (~0, 15, 0), VPKECMD (6) },
{ "pkemark", { PKE_IBIT, SP, PKE_UIMM16 }, MPKECMD, VPKECMD (7) },
{ "flushe", { PKE_IBIT }, MPKECMD, VPKECMD (16) },
{ "flush", { PKE_IBIT }, MPKECMD, VPKECMD (17) },
{ "flusha", { PKE_IBIT }, MPKECMD, VPKECMD (19) },
{ "pkemscal", { PKE_IBIT, SP, PKE_VUADDR }, MPKECMD, VPKECMD (20) },
{ "pkemscnt", { PKE_IBIT }, MPKECMD, VPKECMD (23) },
{ "pkemscalf", { PKE_IBIT, SP, PKE_VUADDR }, MPKECMD, VPKECMD (21) },
/* 2 word instructions */
{ "stmask", { PKE_IBIT, SP, PKE_UIMM32 }, MPKECMD, VPKECMD (32), PKE_OPCODE_LEN2 },
/* 5 word instructions */
{ "strow", { PKE_IBIT, SP, PKE_UIMM32, C, PKE_UIMM32, C, PKE_UIMM32, C, PKE_UIMM32 }, MPKECMD, VPKECMD (48), PKE_OPCODE_LEN5 },
{ "stcol", { PKE_IBIT, SP, PKE_UIMM32, C, PKE_UIMM32, C, PKE_UIMM32, C, PKE_UIMM32 }, MPKECMD, VPKECMD (49), PKE_OPCODE_LEN5 },
/* variable length instructions */
{ "mpg", { PKE_IBIT, SP, PKE_MPGADDR, PKE_VARLENDATA }, MPKECMD, VPKECMD (0x4a), PKE_OPCODE_LENVAR + PKE_OPCODE_MPG },
{ "direct", { PKE_IBIT, SP, PKE_VARLENDATA }, MPKECMD, VPKECMD (0x50), PKE_OPCODE_LENVAR + PKE_OPCODE_DIRECT },
{ "directhl", { PKE_IBIT, SP, PKE_VARLENDATA }, MPKECMD, VPKECMD (0x51), PKE_OPCODE_LENVAR + PKE_OPCODE_DIRECT },
{ "unpack", { PKE_IMRBITS, SP, PKE_UNPACKTYPE, C, PKE_UNPACKADDR, C, PKE_VARLENDATA }, MPKEUNPACK, VPKEUNPACK, PKE_OPCODE_LENVAR + PKE_OPCODE_UNPACK },
};
const int pke_opcodes_count = sizeof (pke_opcodes) / sizeof (pke_opcodes[0]);
/* Non-zero if a variable length instruction was parsed. */
static int state_pke_varlen_p;
/* Length of parsed insn, in 32 bit words, or 0 if unknown. */
static int state_pke_len;
/* PKE parse,insert,extract,print helper fns. */
static long
parse_pke_ibit (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
int flags = 0;
if (*str != '[')
return 0;
for (str = str + 1; *str != ']'; ++str)
{
switch (tolower (*str))
{
case 'i' : flags = 1; break;
default : *errmsg = "unknown flag"; return 0;
}
}
*pstr = str + 1;
return flags;
}
static void
print_pke_ibit (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
if (value)
(*info->fprintf_func) (info->stream, "[i]");
}
static const keyword stmod_modes[] = {
{ PKE_MODE_DIRECT, "direct" },
{ PKE_MODE_ADD, "add" },
{ PKE_MODE_ADDROW, "addrow" },
{ 0, 0 }
};
static long
parse_pke_mode (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
int mode;
char *str = *pstr;
char *start;
char c;
start = str;
str = scan_symbol (str);
c = *str;
*str = 0;
mode = lookup_keyword_value (stmod_modes, start, 0);
*str = c;
if (mode != -1)
{
*pstr = str;
return mode;
}
*errmsg = "invalid mode";
return 0;
}
static void
print_pke_mode (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
disassemble_info *info;
TXVU_INSN *insn;
long value;
{
const char *name = lookup_keyword_name (stmod_modes, value);
if (name)
(*info->fprintf_func) (info->stream, name);
else
(*info->fprintf_func) (info->stream, "???");
}
static long
parse_pke_ability (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
if (strncasecmp (str, "disable", 7) == 0)
{
*pstr += 7;
return 0;
}
else if (strncasecmp (str, "enable", 6) == 0)
{
*pstr += 6;
return 1;
}
*errmsg = "invalid ability";
return 0;
}
static void
print_pke_ability (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
if (value)
(*info->fprintf_func) (info->stream, "enable");
else
(*info->fprintf_func) (info->stream, "disable");
}
static long
parse_pke_mpgaddr (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
char *start;
if (*str == '*')
{
++*pstr;
return 0; /* FIXME:indicate * somehow */
}
start = str;
str = strchr (str, ',');
if (! str)
{
*errmsg = "invalid mpg address";
return 0;
}
/* FIXME: call back to expression() to parse address. */
*pstr = str;
return 0;
}
/* The result here is either the length specified,
or PKE_VARLENDATA_FILE or PKE_VARLENDATA_UNKNOWN. */
static long
parse_pke_varlendata (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
char *start;
if (*str == '*')
{
++*pstr;
return 0; /* FIXME:indicate * somehow */
}
start = str;
/* FIXME: call back to expression() to parse address,
and pick out filename if such. */
*pstr = str + strlen (str);
return 0;
}
static long
parse_pke_imrbits (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
int flags = 0;
if (*str != '[')
return 0;
for (str = str + 1; *str != ']'; ++str)
{
switch (tolower (*str))
{
case 'i' : flags |= PKE_FLAG_I; break;
case 'm' : flags |= PKE_FLAG_M; break;
case 'r' : flags |= PKE_FLAG_R; break;
default : *errmsg = "unknown pke flag"; return 0;
}
}
*pstr = str + 1;
return flags;
}
static void
print_pke_imrbits (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
if (value)
{
(*info->fprintf_func) (info->stream, "[");
if (value & PKE_FLAG_I)
(*info->fprintf_func) (info->stream, "i");
if (value & PKE_FLAG_M)
(*info->fprintf_func) (info->stream, "m");
if (value & PKE_FLAG_R)
(*info->fprintf_func) (info->stream, "r");
(*info->fprintf_func) (info->stream, "]");
}
}
static const keyword unpack_types[] = {
{ PKE_UNPACK_S_32, "s_32" },
{ PKE_UNPACK_S_16, "s_16" },
{ PKE_UNPACK_S_8, "s_8" },
{ PKE_UNPACK_V2_32, "v2_32" },
{ PKE_UNPACK_V2_16, "v2_16" },
{ PKE_UNPACK_V2_8, "v2_8" },
{ PKE_UNPACK_V3_32, "v3_32" },
{ PKE_UNPACK_V3_16, "v3_16" },
{ PKE_UNPACK_V3_8, "v3_8" },
{ PKE_UNPACK_V4_32, "v4_32" },
{ PKE_UNPACK_V4_16, "v4_16" },
{ PKE_UNPACK_V4_8, "v4_8" },
{ PKE_UNPACK_V4_5, "v4_5" },
{ 0, 0 }
};
static long
parse_pke_unpacktype (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
int type;
char *str = *pstr;
char *start;
char c;
start = str;
str = scan_symbol (str);
c = *str;
*str = 0;
type = lookup_keyword_value (unpack_types, start, 0);
*str = c;
if (type != -1)
{
*pstr = str;
return type;
}
*errmsg = "invalid unpack type";
return 0;
}
static void
print_pke_unpacktype (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
const char *name = lookup_keyword_name (unpack_types, value);
if (name)
(*info->fprintf_func) (info->stream, name);
else
(*info->fprintf_func) (info->stream, "???");
}
static long
parse_pke_unpackaddr (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
}
/* External PKE supporting routines. */
/* Return non-zero if the just parsed pke insn has variable length. */
int
pke_varlen_p ()
{
/* This shouldn't be called unless a pke insn was parsed.
Also, we want to catch errors in parsing that don't set this. */
if (state_pke_varlen_p == -1)
abort ();
return state_pke_varlen_p;
}
/* Return length, in 32 bit words, of just parsed pke insn,
or 0 if unknown. */
int
pke_len ()
{
/* This shouldn't be called unless a pke insn was parsed.
Also, we want to catch errors in parsing that don't set this. */
if (state_pke_len == -1)
abort ();
return state_pke_len;
}
/* DMA support. */
PARSE_FN (dma_flags);
INSERT_FN (dma_flags);
EXTRACT_FN (dma_flags);
PRINT_FN (dma_flags);
PARSE_FN (dma_data);
INSERT_FN (dma_data);
EXTRACT_FN (dma_data);
PRINT_FN (dma_data);
PARSE_FN (dma_next);
INSERT_FN (dma_next);
EXTRACT_FN (dma_next);
PRINT_FN (dma_next);
const txvu_operand dma_operands[] =
{
/* place holder (??? not sure if needed) */
#define DMA_UNUSED 128
{ 0 },
/* dma tag flag bits */
#define DMA_FLAGS (DMA_UNUSED + 1)
{ 0, 0, TXVU_OPERAND_SUFFIX,
parse_dma_flags, insert_dma_flags, extract_dma_flags, print_dma_flags },
/* dma data spec */
#define DMA_DATA (DMA_FLAGS + 1)
{ 0, 0, TXVU_OPERAND_DMA_COUNT,
0, insert_dma_data, extract_dma_data, print_dma_data },
/* dma data finalization spec */
#define DMA_DATA2 (DMA_DATA + 1)
{ 0, 0, TXVU_OPERAND_FAKE,
parse_dma_data2, 0, 0, 0},
/* dma next tag spec */
#define DMA_ADDR (DMA_DATA2 + 1)
{ 0, 0, 0,
parse_dma_addr, insert_dma_addr, extract_dma_addr, print_dma_addr},
/* end of list place holder */
{ 0 }
};
struct txvu_opcode dma_opcodes[] =
{
/* ??? Some of these may take optional arguments.
The way to handle that is to have multiple table entries, those with and
those without the optional arguments. */
/*TODO*/ { "dmacall", { DMA_FLAGS, SP, DMA_DATA, C, DMA_ADDR}, 0, 1},
/*TODO*/ { "dmacnt", { DMA_FLAGS, SP, DMA_DATA, C, DMA_ADDR}, 0, 2},
{ "dmaend", { DMA_FLAGS, SP, DMA_DATA, DMA_DATA2}, 0, 3},
{ "dmaend", { DMA_FLAGS, SP, DMA_DATA, C, DMA_ADDR}, 0, 4},
/*TODO*/ { "dmanext", { DMA_FLAGS, SP, DMA_DATA, C, DMA_ADDR}, 0, 5},
/*TODO { "dmaref", { DMA_FLAGS, SP, DMA_DATA, C, DMA_PTR_ADDR}, 0, 6}, */
/*TODO { "dmarefs", { DMA_FLAGS, SP, DMA_DATA, C, DMA_PTR_ADDR}, 0, 7}, */
/*TODO*/ { "dmaret", { DMA_FLAGS, SP, DMA_DATA, DMA_DATA2}, 0, 8}
/*TODO*/ { "dmaret", { DMA_FLAGS, SP, DMA_DATA, C, DMA_ADDR}, 0, 9}
};
const int dma_opcodes_count = sizeof (dma_opcodes) / sizeof (dma_opcodes[0]);
/* Did the current DMA instruction has specify "*" as its length operand?
-1=uninitialized, 0=no, 1=yes. */
static int state_dma_operand_count_p;
/* DMA parse,insert,extract,print helper fns. */
static long
parse_dma_flags (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
int flags = 0;
if (*str != '[')
return 0;
for (str = str + 1; *str != ']'; ++str)
{
switch (tolower (*str))
{
case '0' : flags |= DMA_FLAG_PCE0; break;
case '1' : flags |= DMA_FLAG_PCE1; break;
case 'i' : flags |= DMA_FLAG_INT; break;
case 's' : flags |= DMA_FLAG_SPR; break;
default : *errmsg = "unknown dma flag"; return 0;
}
}
*pstr = str + 1;
return flags;
}
static void
insert_dma_flags (opcode, operand, mods, insn, value, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
long value;
const char **errmsg;
{
}
static long
extract_dma_flags (opcode, operand, mods, insn, pinvalid)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
int *pinvalid;
{
return 0;
}
static void
print_dma_flags (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
if (value)
{
(*info->fprintf_func) (info->stream, "[");
if (value & DMA_FLAG_PCE0)
(*info->fprintf_func) (info->stream, "0");
if (value & DMA_FLAG_PCE1)
(*info->fprintf_func) (info->stream, "1");
if (value & DMA_FLAG_INT)
(*info->fprintf_func) (info->stream, "i");
if (value & DMA_FLAG_SPR)
(*info->fprintf_func) (info->stream, "s");
(*info->fprintf_func) (info->stream, "]");
}
}
static long
parse_dma_data2( opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
/*
If txvu_dma_operand_count() < 0 error
if txvu_dma_operand_count() > 0
if dmaref || dmarefs
compute from two related symbols
else
compute from current addr and end symbol
store value to count field?
*/
}
static void
insert_dma_data (opcode, operand, mods, insn, value, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
long value;
const char **errmsg;
{
}
static long
extract_dma_data (opcode, operand, mods, insn, pinvalid)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
int *pinvalid;
{
return 0;
}
static void
print_dma_data (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
(*info->fprintf_func) (info->stream, "???");
}
static long
parse_dma_addr (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *start = *pstr;
char *end = scan_symbol( start);
if( end == start )
{
*errmsg = "invalid dma next tag";
return 0;
}
/* FIXME: unfinished
if txvu_dma_operand_count() > 0
if dmaref || dmarefs
this operand must be a symbol (vs an expression)
lookup the symbol
store the symbol's value in the addr field (relocs?)
compute the end_symbol's name
lookup the end_symbol
if not found: error
compute the length as _$<name>-<name>
else
evaluate the operand as an expression
store the value to the count field
compute the length as _$EndDma-.
store the count field
else
evaluate the operand as an expression
store the value to the count field
*/
parse_dma_data2( opcode, operand, mods, pstr, errmsg);
*pstr = end;
return 0;
}
static void
insert_dma_addr (opcode, operand, mods, insn, value, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
long value;
const char **errmsg;
{
}
static long
extract_dma_addr (opcode, operand, mods, insn, pinvalid)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
int *pinvalid;
{
return 0;
}
static void
print_dma_addr (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
(*info->fprintf_func) (info->stream, "???");
}
/* GPUIF support. */
PARSE_FN (gpuif_prim);
INSERT_FN (gpuif_prim);
EXTRACT_FN (gpuif_prim);
PRINT_FN (gpuif_prim);
PARSE_FN (gpuif_regs);
INSERT_FN (gpuif_regs);
EXTRACT_FN (gpuif_regs);
PRINT_FN (gpuif_regs);
PARSE_FN (gpuif_nloop);
INSERT_FN (gpuif_nloop);
EXTRACT_FN (gpuif_nloop);
PRINT_FN (gpuif_nloop);
PARSE_FN (gpuif_eop);
INSERT_FN (gpuif_eop);
EXTRACT_FN (gpuif_eop);
PRINT_FN (gpuif_eop);
const txvu_operand gpuif_operands[] =
{
/* place holder (??? not sure if needed) */
#define GPUIF_UNUSED 128
{ 0 },
/* PRIM=foo operand */
#define GPUIF_PRIM (GPUIF_UNUSED + 1)
{ 0, 0, 0, parse_gpuif_prim, insert_gpuif_prim, extract_gpuif_prim, print_gpuif_prim },
/* REGS=foo operand */
#define GPUIF_REGS (GPUIF_PRIM + 1)
{ 0, 0, 0, parse_gpuif_regs, insert_gpuif_regs, extract_gpuif_regs, print_gpuif_regs },
/* NLOOP=foo operand */
#define GPUIF_NLOOP (GPUIF_REGS + 1)
{ 0, 0, 0, parse_gpuif_nloop, insert_gpuif_nloop, extract_gpuif_nloop, print_gpuif_nloop },
/* EOP operand */
#define GPUIF_EOP (GPUIF_NLOOP + 1)
{ 0, 0, 0, parse_gpuif_eop, insert_gpuif_eop, extract_gpuif_eop, print_gpuif_eop },
/* end of list place holder */
{ 0 }
};
struct txvu_opcode gpuif_opcodes[] =
{
/* Some of these may take optional arguments.
The way this is handled is to have multiple table entries, those with and
those without the optional arguments.
!!! The order here is important. The code that scans this table assumes
that if it reaches the end of a syntax string there is nothing more to
parse. This means that longer versions of instructions must appear before
shorter ones. Otherwise the text at the "end" of a longer one may be
interpreted as junk when the parser is using a shorter version of the
syntax string. */
{ "gpuifpacked", { SP, GPUIF_PRIM, C, GPUIF_REGS, C, GPUIF_NLOOP, C, GPUIF_EOP }, 0, 1 },
{ "gpuifpacked", { SP, GPUIF_REGS, C, GPUIF_NLOOP, C, GPUIF_EOP }, 0, 1 },
{ "gpuifpacked", { SP, GPUIF_PRIM, C, GPUIF_REGS, C, GPUIF_EOP }, 0, 1 },
{ "gpuifpacked", { SP, GPUIF_PRIM, C, GPUIF_REGS, C, GPUIF_NLOOP }, 0, 1 },
{ "gpuifpacked", { SP, GPUIF_REGS, C, GPUIF_EOP }, 0, 1 },
{ "gpuifpacked", { SP, GPUIF_REGS, C, GPUIF_NLOOP }, 0, 1 },
{ "gpuifpacked", { SP, GPUIF_PRIM, C, GPUIF_REGS }, 0, 1 },
{ "gpuifpacked", { SP, GPUIF_REGS }, 0, 1 },
{ "gpuifreglist", { SP, GPUIF_REGS, C, GPUIF_NLOOP, C, GPUIF_EOP }, 0, 2 },
{ "gpuifreglist", { SP, GPUIF_REGS, C, GPUIF_EOP }, 0, 2 },
{ "gpuifreglist", { SP, GPUIF_REGS, C, GPUIF_NLOOP }, 0, 2 },
{ "gpuifreglist", { SP, GPUIF_REGS }, 0, 2 },
{ "gpuifimage", { SP, GPUIF_NLOOP }, 0, 3 },
{ "gpuifimage", { 0 }, 0, 3 },
};
const int gpuif_opcodes_count = sizeof (gpuif_opcodes) / sizeof (gpuif_opcodes[0]);
/* GPUIF parse,insert,extract,print helper fns. */
static long
parse_gpuif_prim (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
char *start;
long prim;
if (strncasecmp (str, "prim=", 5) != 0)
{
*errmsg = "missing PRIM spec";
return 0;
}
str += 5;
for (start = str; isalnum (*str); ++str)
continue;
if (str == start)
{
*errmsg = "missing PRIM spec";
return 0;
}
/* FIXME: Yes, atoi doesn't do error checking. Later. */
prim = atoi (start);
*pstr = str;
return prim;
}
static void
insert_gpuif_prim (opcode, operand, mods, insn, value, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
long value;
const char **errmsg;
{
}
static long
extract_gpuif_prim (opcode, operand, mods, insn, pinvalid)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
int *pinvalid;
{
return 0;
}
static void
print_gpuif_prim (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
(*info->fprintf_func) (info->stream, "???");
}
static const keyword gpuif_regs[] = {
{ GPUIF_REG_PRIM, "prim" },
{ GPUIF_REG_RGBAQ, "rgbaq" },
{ GPUIF_REG_ST, "st" },
{ GPUIF_REG_UV, "uv" },
{ GPUIF_REG_XYZF2, "xyzf2" },
{ GPUIF_REG_TEXCLUT_1, "texclut_1" },
{ GPUIF_REG_TEXCLUT_2, "texclut_2" },
{ GPUIF_REG_TEX0_1, "tex0_1" },
{ GPUIF_REG_TEX0_2, "tex0_2" },
{ GPUIF_REG_TEX1_1, "tex1_1" },
{ GPUIF_REG_TEX1_2, "tex1_2" },
{ GPUIF_REG_XYZF3, "xyzf3" },
{ GPUIF_REG_PRMODE, "prmode" },
{ GPUIF_REG_A_D, "a_d" },
{ GPUIF_REG_NOP, "nop" },
{ 0, 0 }
};
/* Parse a REGS= spec.
The result is ???. */
static long
parse_gpuif_regs (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
char *start;
char c;
int reg;
if (strncasecmp (str, "regs=", 5) != 0)
{
*errmsg = "missing REGS spec";
return 0;
}
str += 5;
SKIP_BLANKS (str);
if (*str != '{')
{
*errmsg = "missing '{' in REGS spec";
return 0;
}
++str;
while (*str && *str != '}')
{
/* Pick out the register name. */
SKIP_BLANKS (str);
start = str;
str = scan_symbol (str);
if (str == start)
{
*errmsg = "invalid REG";
return 0;
}
/* Look it up in the table. */
c = *str;
*str = 0;
reg = lookup_keyword_value (gpuif_regs, start, 0);
*str = c;
if (reg == -1)
{
*errmsg = "invalid REG";
return 0;
}
/* FIXME: save `reg' away somewhere */
/* Prepare for the next one. */
SKIP_BLANKS (str);
if (*str == ',')
++str;
else if (*str != '}')
break;
}
if (*str != '}')
{
*errmsg = "missing '{' in REGS spec";
return 0;
}
*pstr = str + 1;
return 0; /* FIXME */
}
static void
insert_gpuif_regs (opcode, operand, mods, insn, value, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
long value;
const char **errmsg;
{
}
static long
extract_gpuif_regs (opcode, operand, mods, insn, pinvalid)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
int *pinvalid;
{
return 0;
}
static void
print_gpuif_regs (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
(*info->fprintf_func) (info->stream, "???");
}
static long
parse_gpuif_nloop (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
char *str = *pstr;
char *start;
char c;
int nloop;
if (strncasecmp (str, "nloop=", 6) != 0)
{
*errmsg = "missing NLOOP spec";
return 0;
}
str += 6;
SKIP_BLANKS (str);
start = str;
str = scan_symbol (str);
if (str == start)
{
*errmsg = "invalid NOOP spec";
return 0;
}
/* FIXME: error checking */
nloop = atoi (start);
*pstr = str;
return nloop;
}
static void
insert_gpuif_nloop (opcode, operand, mods, insn, value, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
long value;
const char **errmsg;
{
}
static long
extract_gpuif_nloop (opcode, operand, mods, insn, pinvalid)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
int *pinvalid;
{
return 0;
}
static void
print_gpuif_nloop (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
(*info->fprintf_func) (info->stream, "???");
}
static long
parse_gpuif_eop (opcode, operand, mods, pstr, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
char **pstr;
const char **errmsg;
{
if (strncasecmp (*pstr, "eop", 3) == 0)
{
*pstr += 3;
return 1;
}
*errmsg = "missing `EOP'";
return 0;
}
static void
insert_gpuif_eop (opcode, operand, mods, insn, value, errmsg)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
long value;
const char **errmsg;
{
}
static long
extract_gpuif_eop (opcode, operand, mods, insn, pinvalid)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
int *pinvalid;
{
return 0;
}
static void
print_gpuif_eop (opcode, operand, mods, insn, info, value)
const txvu_opcode *opcode;
const txvu_operand *operand;
int mods;
TXVU_INSN *insn;
disassemble_info *info;
long value;
{
(*info->fprintf_func) (info->stream, "???");
}
/* Init fns.
These are called before doing each of the respective activities. */
/* Called by the assembler before parsing an instruction. */
void
txvu_opcode_init_parse ()
{
state_vu_mnemonic_dest = -1;
state_vu_mnemonic_bc = -1;
state_pke_varlen_p = -1;
state_pke_len = -1;
state_dma_operand_count_p = -1;
}
/*
Query or set the current type of a DMA length operand, explicit or computed by "as".
The return value is the setting before "action" is applied:
-1=uninitialized, 0=explicit, +1=computed
The value of "action" is interpreted as:
-1=no change, 0=set explicit, +1=set computed
*/
int
txvu_dma_operand_count( action)
{
int result = state_dma_operand_count;
if( action == 0)
state_dma_operand_count = 0;
else if( action > 0)
state_dma_operand_count_p = 1;
return result;
}
/* Called by the disassembler before printing an instruction. */
void
txvu_opcode_init_print ()
{
state_vu_mnemonic_dest = -1;
state_vu_mnemonic_bc = -1;
state_pke_varlen_p = -1;
state_pke_len = -1;
}
/* Indexed by first letter of opcode. Points to chain of opcodes with same
first letter. */
/* ??? One can certainly use a better hash. Later. */
static txvu_opcode *upper_opcode_map[26 + 1];
static txvu_opcode *lower_opcode_map[26 + 1];
/* Indexed by insn code. Points to chain of opcodes with same insn code. */
static txvu_opcode *upper_icode_map[(1 << TXVU_ICODE_HASH_SIZE) - 1];
static txvu_opcode *lower_icode_map[(1 << TXVU_ICODE_HASH_SIZE) - 1];
/* Initialize any tables that need it.
Must be called once at start up (or when first needed).
FLAGS is currently unused but is intended to control initialization. */
void
txvu_opcode_init_tables (flags)
int flags;
{
static int init_p = 0;
/* We may be intentionally called more than once (for example gdb will call
us each time the user switches cpu). These tables only need to be init'd
once though. */
/* ??? We can remove the need for txvu_opcode_supported by taking it into
account here, but I'm not sure I want to do that yet (if ever). */
if (!init_p)
{
int i,n;
/* Upper VU table. */
memset (upper_opcode_map, 0, sizeof (upper_opcode_map));
memset (upper_icode_map, 0, sizeof (upper_icode_map));
/* Scan the table backwards so macros appear at the front. */
for (i = txvu_upper_opcodes_count - 1; i >= 0; --i)
{
int opcode_hash = TXVU_HASH_UPPER_OPCODE (txvu_upper_opcodes[i].mnemonic);
int icode_hash = TXVU_HASH_UPPER_ICODE (txvu_upper_opcodes[i].value);
txvu_upper_opcodes[i].next_asm = upper_opcode_map[opcode_hash];
upper_opcode_map[opcode_hash] = &txvu_upper_opcodes[i];
txvu_upper_opcodes[i].next_dis = upper_icode_map[icode_hash];
upper_icode_map[icode_hash] = &txvu_upper_opcodes[i];
}
/* Lower VU table. */
memset (lower_opcode_map, 0, sizeof (lower_opcode_map));
memset (lower_icode_map, 0, sizeof (lower_icode_map));
/* Scan the table backwards so macros appear at the front. */
for (i = txvu_lower_opcodes_count - 1; i >= 0; --i)
{
int opcode_hash = TXVU_HASH_LOWER_OPCODE (txvu_lower_opcodes[i].mnemonic);
int icode_hash = TXVU_HASH_LOWER_ICODE (txvu_lower_opcodes[i].value);
txvu_lower_opcodes[i].next_asm = lower_opcode_map[opcode_hash];
lower_opcode_map[opcode_hash] = &txvu_lower_opcodes[i];
txvu_lower_opcodes[i].next_dis = lower_icode_map[icode_hash];
lower_icode_map[icode_hash] = &txvu_lower_opcodes[i];
}
/* FIXME: We just hash everything to the same value for the rest.
Quick hack while other things are worked on. */
/* PKE table. */
for (i = pke_opcodes_count - 2; i >= 0; --i)
{
pke_opcodes[i].next_asm = & pke_opcodes[i+1];
pke_opcodes[i].next_dis = & pke_opcodes[i+1];
}
/* DMA table. */
for (i = dma_opcodes_count - 2; i >= 0; --i)
{
dma_opcodes[i].next_asm = & dma_opcodes[i+1];
dma_opcodes[i].next_dis = & dma_opcodes[i+1];
}
/* GPUIF table. */
for (i = gpuif_opcodes_count - 2; i >= 0; --i)
{
gpuif_opcodes[i].next_asm = & gpuif_opcodes[i+1];
gpuif_opcodes[i].next_dis = & gpuif_opcodes[i+1];
}
init_p = 1;
}
}
/* Return the first insn in the chain for assembling upper INSN. */
const txvu_opcode *
txvu_upper_opcode_lookup_asm (insn)
const char *insn;
{
return upper_opcode_map[TXVU_HASH_UPPER_OPCODE (insn)];
}
/* Return the first insn in the chain for disassembling upper INSN. */
const txvu_opcode *
txvu_upper_opcode_lookup_dis (insn)
TXVU_INSN insn;
{
return upper_icode_map[TXVU_HASH_UPPER_ICODE (insn)];
}
/* Return the first insn in the chain for assembling lower INSN. */
const txvu_opcode *
txvu_lower_opcode_lookup_asm (insn)
const char *insn;
{
return lower_opcode_map[TXVU_HASH_LOWER_OPCODE (insn)];
}
/* Return the first insn in the chain for disassembling lower INSN. */
const txvu_opcode *
txvu_lower_opcode_lookup_dis (insn)
TXVU_INSN insn;
{
return lower_icode_map[TXVU_HASH_LOWER_ICODE (insn)];
}
/* Return the first insn in the chain for assembling lower INSN. */
const txvu_opcode *
pke_opcode_lookup_asm (insn)
const char *insn;
{
return &pke_opcodes[0];
}
/* Return the first insn in the chain for disassembling lower INSN. */
const txvu_opcode *
pke_opcode_lookup_dis (insn)
TXVU_INSN insn;
{
return &pke_opcodes[0];
}
/* Return the first insn in the chain for assembling lower INSN. */
const txvu_opcode *
dma_opcode_lookup_asm (insn)
const char *insn;
{
return &dma_opcodes[0];
}
/* Return the first insn in the chain for disassembling lower INSN. */
const txvu_opcode *
dma_opcode_lookup_dis (insn)
TXVU_INSN insn;
{
return &dma_opcodes[0];
}
/* Return the first insn in the chain for assembling lower INSN. */
const txvu_opcode *
gpuif_opcode_lookup_asm (insn)
const char *insn;
{
return &gpuif_opcodes[0];
}
/* Return the first insn in the chain for disassembling lower INSN. */
const txvu_opcode *
gpuif_opcode_lookup_dis (insn)
TXVU_INSN insn;
{
return &gpuif_opcodes[0];
}
/* Misc. utilities. */
/* Scan a symbol and return a pointer to one past the end. */
static char *
scan_symbol (sym)
char *sym;
{
while (*sym && issymchar (*sym))
++sym;
return sym;
}
/* Given a keyword, look up its value, or -1 if not found. */
static int
lookup_keyword_value (table, name, case_sensitive_p)
const keyword *table;
const char *name;
int case_sensitive_p;
{
const keyword *p;
if (case_sensitive_p)
{
for (p = table; p->name; ++p)
if (strcmp (name, p->name) == 0)
return p->value;
}
else
{
for (p = table; p->name; ++p)
if (strcasecmp (name, p->name) == 0)
return p->value;
}
return -1;
}
/* Given a keyword's value, look up its name, or NULL if not found. */
static const char *
lookup_keyword_name (table, value)
const keyword *table;
int value;
{
const keyword *p;
for (p = table; p->name; ++p)
if (value == p->value)
return p->name;
return NULL;
}