📄 maverick.c
字号:
/* maverick.c -- Cirrus/DSP co-processor interface.
Copyright (C) 2003 Free Software Foundation, Inc.
Contributed by Aldy Hernandez (aldyh@redhat.com).
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 of the License, 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 <assert.h>
#include "armdefs.h"
#include "ansidecl.h"
#include "armemu.h"
/*#define CIRRUS_DEBUG 1 /**/
#if CIRRUS_DEBUG
# define printfdbg printf
#else
# define printfdbg printf_nothing
#endif
#define POS64(i) ( (~(i)) >> 63 )
#define NEG64(i) ( (i) >> 63 )
/* Define Co-Processor instruction handlers here. */
/* Here's ARMulator's DSP definition. A few things to note:
1) it has 16 64-bit registers and 4 72-bit accumulators
2) you can only access its registers with MCR and MRC. */
/* We can't define these in here because this file might not be linked
unless the target is arm9e-*. They are defined in wrapper.c.
Eventually the simulator should be made to handle any coprocessor
at run time. */
struct maverick_regs
{
union
{
int i;
float f;
} upper;
union
{
int i;
float f;
} lower;
};
union maverick_acc_regs
{
long double ld; /* Acc registers are 72-bits. */
};
struct maverick_regs DSPregs[16];
union maverick_acc_regs DSPacc[4];
ARMword DSPsc;
#define DEST_REG (BITS (12, 15))
#define SRC1_REG (BITS (16, 19))
#define SRC2_REG (BITS (0, 3))
static int lsw_int_index, msw_int_index;
static int lsw_float_index, msw_float_index;
static double mv_getRegDouble (int);
static long long mv_getReg64int (int);
static void mv_setRegDouble (int, double val);
static void mv_setReg64int (int, long long val);
static union
{
double d;
long long ll;
int ints[2];
} reg_conv;
static void
printf_nothing (void *foo, ...)
{
}
static void
cirrus_not_implemented (char *insn)
{
fprintf (stderr, "Cirrus instruction '%s' not implemented.\n", insn);
fprintf (stderr, "aborting!\n");
exit (1);
}
static unsigned
DSPInit (ARMul_State * state)
{
ARMul_ConsolePrint (state, ", DSP present");
return TRUE;
}
unsigned
DSPMRC4 (ARMul_State * state ATTRIBUTE_UNUSED,
unsigned type ATTRIBUTE_UNUSED, ARMword instr, ARMword * value)
{
switch (BITS (5, 7))
{
case 0: /* cfmvrdl */
/* Move lower half of a DF stored in a DSP reg into an Arm reg. */
printfdbg ("cfmvrdl\n");
printfdbg ("\tlower half=0x%x\n", DSPregs[SRC1_REG].lower.i);
printfdbg ("\tentire thing=%g\n", mv_getRegDouble (SRC1_REG));
*value = (ARMword) DSPregs[SRC1_REG].lower.i;
break;
case 1: /* cfmvrdh */
/* Move upper half of a DF stored in a DSP reg into an Arm reg. */
printfdbg ("cfmvrdh\n");
printfdbg ("\tupper half=0x%x\n", DSPregs[SRC1_REG].upper.i);
printfdbg ("\tentire thing=%g\n", mv_getRegDouble (SRC1_REG));
*value = (ARMword) DSPregs[SRC1_REG].upper.i;
break;
case 2: /* cfmvrs */
/* Move SF from upper half of a DSP register to an Arm register. */
*value = (ARMword) DSPregs[SRC1_REG].upper.i;
printfdbg ("cfmvrs = mvf%d <-- %f\n",
SRC1_REG, DSPregs[SRC1_REG].upper.f);
break;
#ifdef doesnt_work
case 4: /* cfcmps */
{
float a, b;
int n, z, c, v;
a = DSPregs[SRC1_REG].upper.f;
b = DSPregs[SRC2_REG].upper.f;
printfdbg ("cfcmps\n");
printfdbg ("\tcomparing %f and %f\n", a, b);
z = a == b; /* zero */
n = a != b; /* negative */
v = a > b; /* overflow */
c = 0; /* carry */
*value = (n << 31) | (z << 30) | (c << 29) | (v << 28);
break;
}
case 5: /* cfcmpd */
{
double a, b;
int n, z, c, v;
a = mv_getRegDouble (SRC1_REG);
b = mv_getRegDouble (SRC2_REG);
printfdbg ("cfcmpd\n");
printfdbg ("\tcomparing %g and %g\n", a, b);
z = a == b; /* zero */
n = a != b; /* negative */
v = a > b; /* overflow */
c = 0; /* carry */
*value = (n << 31) | (z << 30) | (c << 29) | (v << 28);
break;
}
#else
case 4: /* cfcmps */
{
float a, b;
int n, z, c, v;
a = DSPregs[SRC1_REG].upper.f;
b = DSPregs[SRC2_REG].upper.f;
printfdbg ("cfcmps\n");
printfdbg ("\tcomparing %f and %f\n", a, b);
z = a == b; /* zero */
n = a < b; /* negative */
c = a > b; /* carry */
v = 0; /* fixme */
printfdbg ("\tz = %d, n = %d\n", z, n);
*value = (n << 31) | (z << 30) | (c << 29) | (v << 28);
break;
}
case 5: /* cfcmpd */
{
double a, b;
int n, z, c, v;
a = mv_getRegDouble (SRC1_REG);
b = mv_getRegDouble (SRC2_REG);
printfdbg ("cfcmpd\n");
printfdbg ("\tcomparing %g and %g\n", a, b);
z = a == b; /* zero */
n = a < b; /* negative */
c = a > b; /* carry */
v = 0; /* fixme */
*value = (n << 31) | (z << 30) | (c << 29) | (v << 28);
break;
}
#endif
default:
fprintf (stderr, "unknown opcode in DSPMRC4 0x%x\n", instr);
cirrus_not_implemented ("unknown");
break;
}
return ARMul_DONE;
}
unsigned
DSPMRC5 (ARMul_State * state ATTRIBUTE_UNUSED,
unsigned type ATTRIBUTE_UNUSED, ARMword instr, ARMword * value)
{
switch (BITS (5, 7))
{
case 0: /* cfmvr64l */
/* Move lower half of 64bit int from Cirrus to Arm. */
*value = (ARMword) DSPregs[SRC1_REG].lower.i;
printfdbg ("cfmvr64l ARM_REG = mvfx%d <-- %d\n",
DEST_REG, (int) *value);
break;
case 1: /* cfmvr64h */
/* Move upper half of 64bit int from Cirrus to Arm. */
*value = (ARMword) DSPregs[SRC1_REG].upper.i;
printfdbg ("cfmvr64h <-- %d\n", (int) *value);
break;
case 4: /* cfcmp32 */
{
int res;
int n, z, c, v;
unsigned int a, b;
printfdbg ("cfcmp32 mvfx%d - mvfx%d\n", SRC1_REG, SRC2_REG);
/* FIXME: see comment for cfcmps. */
a = DSPregs[SRC1_REG].lower.i;
b = DSPregs[SRC2_REG].lower.i;
res = DSPregs[SRC1_REG].lower.i - DSPregs[SRC2_REG].lower.i;
/* zero */
z = res == 0;
/* negative */
n = res < 0;
/* overflow */
v = SubOverflow (DSPregs[SRC1_REG].lower.i, DSPregs[SRC2_REG].lower.i,
res);
/* carry */
c = (NEG (a) && POS (b) ||
(NEG (a) && POS (res)) || (POS (b) && POS (res)));
*value = (n << 31) | (z << 30) | (c << 29) | (v << 28);
break;
}
case 5: /* cfcmp64 */
{
long long res;
int n, z, c, v;
unsigned long long a, b;
printfdbg ("cfcmp64 mvdx%d - mvdx%d\n", SRC1_REG, SRC2_REG);
/* fixme: see comment for cfcmps. */
a = mv_getReg64int (SRC1_REG);
b = mv_getReg64int (SRC2_REG);
res = mv_getReg64int (SRC1_REG) - mv_getReg64int (SRC2_REG);
/* zero */
z = res == 0;
/* negative */
n = res < 0;
/* overflow */
v = ((NEG64 (a) && POS64 (b) && POS64 (res))
|| (POS64 (a) && NEG64 (b) && NEG64 (res)));
/* carry */
c = (NEG64 (a) && POS64 (b) ||
(NEG64 (a) && POS64 (res)) || (POS64 (b) && POS64 (res)));
*value = (n << 31) | (z << 30) | (c << 29) | (v << 28);
break;
}
default:
fprintf (stderr, "unknown opcode in DSPMRC5 0x%x\n", instr);
cirrus_not_implemented ("unknown");
break;
}
return ARMul_DONE;
}
unsigned
DSPMRC6 (ARMul_State * state ATTRIBUTE_UNUSED,
unsigned type ATTRIBUTE_UNUSED, ARMword instr, ARMword * value)
{
switch (BITS (5, 7))
{
case 0: /* cfmval32 */
cirrus_not_implemented ("cfmval32");
break;
case 1: /* cfmvam32 */
cirrus_not_implemented ("cfmvam32");
break;
case 2: /* cfmvah32 */
cirrus_not_implemented ("cfmvah32");
break;
case 3: /* cfmva32 */
cirrus_not_implemented ("cfmva32");
break;
case 4: /* cfmva64 */
cirrus_not_implemented ("cfmva64");
break;
case 5: /* cfmvsc32 */
cirrus_not_implemented ("cfmvsc32");
break;
default:
fprintf (stderr, "unknown opcode in DSPMRC6 0x%x\n", instr);
cirrus_not_implemented ("unknown");
break;
}
return ARMul_DONE;
}
unsigned
DSPMCR4 (ARMul_State * state,
unsigned type ATTRIBUTE_UNUSED, ARMword instr, ARMword value)
{
switch (BITS (5, 7))
{
case 0: /* cfmvdlr */
/* Move the lower half of a DF value from an Arm register into
the lower half of a Cirrus register. */
printfdbg ("cfmvdlr <-- 0x%x\n", (int) value);
DSPregs[SRC1_REG].lower.i = (int) value;
break;
case 1: /* cfmvdhr */
/* Move the upper half of a DF value from an Arm register into
the upper half of a Cirrus register. */
printfdbg ("cfmvdhr <-- 0x%x\n", (int) value);
DSPregs[SRC1_REG].upper.i = (int) value;
break;
case 2: /* cfmvsr */
/* Move SF from Arm register into upper half of Cirrus register. */
printfdbg ("cfmvsr <-- 0x%x\n", (int) value);
DSPregs[SRC1_REG].upper.i = (int) value;
break;
default:
fprintf (stderr, "unknown opcode in DSPMCR4 0x%x\n", instr);
cirrus_not_implemented ("unknown");
break;
}
return ARMul_DONE;
}
unsigned
DSPMCR5 (ARMul_State * state,
unsigned type ATTRIBUTE_UNUSED, ARMword instr, ARMword value)
{
union
{
int s;
unsigned int us;
} val;
switch (BITS (5, 7))
{
case 0: /* cfmv64lr */
/* Move lower half of a 64bit int from an ARM register into the
lower half of a DSP register and sign extend it. */
printfdbg ("cfmv64lr mvdx%d <-- 0x%x\n", SRC1_REG, (int) value);
DSPregs[SRC1_REG].lower.i = (int) value;
break;
case 1: /* cfmv64hr */
/* Move upper half of a 64bit int from an ARM register into the
upper half of a DSP register. */
printfdbg ("cfmv64hr ARM_REG = mvfx%d <-- 0x%x\n",
SRC1_REG, (int) value);
DSPregs[SRC1_REG].upper.i = (int) value;
break;
case 2: /* cfrshl32 */
printfdbg ("cfrshl32\n");
val.us = value;
if (val.s > 0)
DSPregs[SRC2_REG].lower.i = DSPregs[SRC1_REG].lower.i << value;
else
DSPregs[SRC2_REG].lower.i = DSPregs[SRC1_REG].lower.i >> -value;
break;
case 3: /* cfrshl64 */
printfdbg ("cfrshl64\n");
val.us = value;
if (val.s > 0)
mv_setReg64int (SRC2_REG, mv_getReg64int (SRC1_REG) << value);
else
mv_setReg64int (SRC2_REG, mv_getReg64int (SRC1_REG) >> -value);
break;
default:
fprintf (stderr, "unknown opcode in DSPMCR5 0x%x\n", instr);
cirrus_not_implemented ("unknown");
break;
}
return ARMul_DONE;
}
unsigned
DSPMCR6 (ARMul_State * state,
unsigned type ATTRIBUTE_UNUSED, ARMword instr, ARMword value)
{
switch (BITS (5, 7))
{
case 0: /* cfmv32al */
cirrus_not_implemented ("cfmv32al");
break;
case 1: /* cfmv32am */
cirrus_not_implemented ("cfmv32am");
break;
case 2: /* cfmv32ah */
cirrus_not_implemented ("cfmv32ah");
break;
case 3: /* cfmv32a */
cirrus_not_implemented ("cfmv32a");
break;
case 4: /* cfmv64a */
cirrus_not_implemented ("cfmv64a");
break;
case 5: /* cfmv32sc */
cirrus_not_implemented ("cfmv32sc");
break;
default:
fprintf (stderr, "unknown opcode in DSPMCR6 0x%x\n", instr);
cirrus_not_implemented ("unknown");
break;
}
return ARMul_DONE;
}
unsigned
DSPLDC4 (ARMul_State * state ATTRIBUTE_UNUSED,
unsigned type, ARMword instr, ARMword data)
{
static unsigned words;
if (type != ARMul_DATA)
{
words = 0;
return ARMul_DONE;
}
if (BIT (22))
{ /* it's a long access, get two words */
/* cfldrd */
printfdbg ("cfldrd: %x (words = %d) (bigend = %d) DESTREG = %d\n",
data, words, state->bigendSig, DEST_REG);
if (words == 0)
{
if (state->bigendSig)
DSPregs[DEST_REG].upper.i = (int) data;
else
DSPregs[DEST_REG].lower.i = (int) data;
}
else
{
if (state->bigendSig)
DSPregs[DEST_REG].lower.i = (int) data;
else
DSPregs[DEST_REG].upper.i = (int) data;
}
++words;
if (words == 2)
{
printfdbg ("\tmvd%d <-- mem = %g\n", DEST_REG,
mv_getRegDouble (DEST_REG));
return ARMul_DONE;
}
else
return ARMul_INC;
}
else
{
/* Get just one word. */
/* cfldrs */
printfdbg ("cfldrs\n");
DSPregs[DEST_REG].upper.i = (int) data;
printfdbg ("\tmvf%d <-- mem = %f\n", DEST_REG,
DSPregs[DEST_REG].upper.f);
return ARMul_DONE;
}
}
unsigned
DSPLDC5 (ARMul_State * state ATTRIBUTE_UNUSED,
unsigned type, ARMword instr, ARMword data)
{
static unsigned words;
if (type != ARMul_DATA)
{
words = 0;
return ARMul_DONE;
}
if (BIT (22))
{
/* It's a long access, get two words. */
/* cfldr64 */
printfdbg ("cfldr64: %d\n", data);
if (words == 0)
{
if (state->bigendSig)
DSPregs[DEST_REG].upper.i = (int) data;
else
DSPregs[DEST_REG].lower.i = (int) data;
}
else
{
if (state->bigendSig)
DSPregs[DEST_REG].lower.i = (int) data;
else
DSPregs[DEST_REG].upper.i = (int) data;
}
++words;
if (words == 2)
{
printfdbg ("\tmvdx%d <-- mem = %lld\n", DEST_REG,
mv_getReg64int (DEST_REG));
return ARMul_DONE;
}
else
return ARMul_INC;
}
else
{
/* Get just one word. */
/* cfldr32 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -