blob: 48be6806ea22a059c83b1b065a3643c0be62037c [file] [log] [blame]
/* armcopro.c -- co-processor interface: ARM6 Instruction Emulator.
Copyright (C) 1994, 2000 Advanced RISC Machines Ltd.
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 "armdefs.h"
#include "armemu.h"
#include "ansidecl.h"
extern unsigned ARMul_CoProInit (ARMul_State * state);
extern void ARMul_CoProExit (ARMul_State * state);
extern void ARMul_CoProAttach (ARMul_State * state, unsigned number,
ARMul_CPInits * init, ARMul_CPExits * exit,
ARMul_LDCs * ldc, ARMul_STCs * stc,
ARMul_MRCs * mrc, ARMul_MCRs * mcr,
ARMul_CDPs * cdp,
ARMul_CPReads * read, ARMul_CPWrites * write);
extern void ARMul_CoProDetach (ARMul_State * state, unsigned number);
/***************************************************************************\
* Dummy Co-processors *
\***************************************************************************/
static unsigned NoCoPro3R (ARMul_State * state, unsigned, ARMword);
static unsigned NoCoPro4R (ARMul_State * state, unsigned, ARMword, ARMword);
static unsigned NoCoPro4W (ARMul_State * state, unsigned, ARMword, ARMword *);
/***************************************************************************\
* Define Co-Processor instruction handlers here *
\***************************************************************************/
/* Here's ARMulator's MMU definition. A few things to note:
1) it has eight registers, but only two are defined.
2) you can only access its registers with MCR and MRC.
3) MMU Register 0 (ID) returns 0x41440110
4) Register 1 only has 4 bits defined. Bits 0 to 3 are unused, bit 4
controls 32/26 bit program space, bit 5 controls 32/26 bit data space,
bit 6 controls late abort timimg and bit 7 controls big/little endian.
*/
static ARMword MMUReg[8];
static unsigned
MMUInit (ARMul_State * state)
{
MMUReg[1] = state->prog32Sig << 4 |
state->data32Sig << 5 | state->lateabtSig << 6 | state->bigendSig << 7;
ARMul_ConsolePrint (state, ", MMU present");
return (TRUE);
}
static unsigned
MMUMRC (ARMul_State * state ATTRIBUTE_UNUSED, unsigned type ATTRIBUTE_UNUSED, ARMword instr, ARMword * value)
{
int reg = BITS (16, 19) & 7;
if (reg == 0)
*value = 0x41440110;
else
*value = MMUReg[reg];
return (ARMul_DONE);
}
static unsigned
MMUMCR (ARMul_State * state, unsigned type ATTRIBUTE_UNUSED, ARMword instr, ARMword value)
{
int reg = BITS (16, 19) & 7;
MMUReg[reg] = value;
if (reg == 1)
{
ARMword p,d,l,b;
p = state->prog32Sig;
d = state->data32Sig;
l = state->lateabtSig;
b = state->bigendSig;
state->prog32Sig = value >> 4 & 1;
state->data32Sig = value >> 5 & 1;
state->lateabtSig = value >> 6 & 1;
state->bigendSig = value >> 7 & 1;
if (p != state->prog32Sig
|| d != state->data32Sig
|| l != state->lateabtSig
|| b != state->bigendSig)
state->Emulate = CHANGEMODE; /* Force ARMulator to notice these now. */
}
return ARMul_DONE;
}
static unsigned
MMURead (ARMul_State * state ATTRIBUTE_UNUSED, unsigned reg, ARMword * value)
{
if (reg == 0)
*value = 0x41440110;
else if (reg < 8)
*value = MMUReg[reg];
return (TRUE);
}
static unsigned
MMUWrite (ARMul_State * state, unsigned reg, ARMword value)
{
if (reg < 8)
MMUReg[reg] = value;
if (reg == 1)
{
ARMword p,d,l,b;
p = state->prog32Sig;
d = state->data32Sig;
l = state->lateabtSig;
b = state->bigendSig;
state->prog32Sig = value >> 4 & 1;
state->data32Sig = value >> 5 & 1;
state->lateabtSig = value >> 6 & 1;
state->bigendSig = value >> 7 & 1;
if (p != state->prog32Sig
|| d != state->data32Sig
|| l != state->lateabtSig
|| b != state->bigendSig)
state->Emulate = CHANGEMODE; /* Force ARMulator to notice these now. */
}
return TRUE;
}
/* What follows is the Validation Suite Coprocessor. It uses two
co-processor numbers (4 and 5) and has the follwing functionality.
Sixteen registers. Both co-processor nuimbers can be used in an MCR and
MRC to access these registers. CP 4 can LDC and STC to and from the
registers. CP 4 and CP 5 CDP 0 will busy wait for the number of cycles
specified by a CP register. CP 5 CDP 1 issues a FIQ after a number of
cycles (specified in a CP register), CDP 2 issues an IRQW in the same
way, CDP 3 and 4 turn of the FIQ and IRQ source, and CDP 5 stores a 32
bit time value in a CP register (actually it's the total number of N, S,
I, C and F cyles) */
static ARMword ValReg[16];
static unsigned
ValLDC (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 */
ValReg[BITS (12, 15)] = data;
if (words++ == 4)
return (ARMul_DONE);
else
return (ARMul_INC);
}
else
{ /* get just one word */
ValReg[BITS (12, 15)] = data;
return (ARMul_DONE);
}
}
static unsigned
ValSTC (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 */
*data = ValReg[BITS (12, 15)];
if (words++ == 4)
return (ARMul_DONE);
else
return (ARMul_INC);
}
else
{ /* get just one word */
*data = ValReg[BITS (12, 15)];
return (ARMul_DONE);
}
}
static unsigned
ValMRC (ARMul_State * state ATTRIBUTE_UNUSED, unsigned type ATTRIBUTE_UNUSED, ARMword instr, ARMword * value)
{
*value = ValReg[BITS (16, 19)];
return (ARMul_DONE);
}
static unsigned
ValMCR (ARMul_State * state ATTRIBUTE_UNUSED, unsigned type ATTRIBUTE_UNUSED, ARMword instr, ARMword value)
{
ValReg[BITS (16, 19)] = value;
return (ARMul_DONE);
}
static unsigned
ValCDP (ARMul_State * state, unsigned type, ARMword instr)
{
static unsigned long finish = 0;
ARMword howlong;
howlong = ValReg[BITS (0, 3)];
if (BITS (20, 23) == 0)
{
if (type == ARMul_FIRST)
{ /* First cycle of a busy wait */
finish = ARMul_Time (state) + howlong;
if (howlong == 0)
return (ARMul_DONE);
else
return (ARMul_BUSY);
}
else if (type == ARMul_BUSY)
{
if (ARMul_Time (state) >= finish)
return (ARMul_DONE);
else
return (ARMul_BUSY);
}
}
return (ARMul_CANT);
}
static unsigned
DoAFIQ (ARMul_State * state)
{
state->NfiqSig = LOW;
state->Exception++;
return (0);
}
static unsigned
DoAIRQ (ARMul_State * state)
{
state->NirqSig = LOW;
state->Exception++;
return (0);
}
static unsigned
IntCDP (ARMul_State * state, unsigned type, ARMword instr)
{
static unsigned long finish;
ARMword howlong;
howlong = ValReg[BITS (0, 3)];
switch ((int) BITS (20, 23))
{
case 0:
if (type == ARMul_FIRST)
{ /* First cycle of a busy wait */
finish = ARMul_Time (state) + howlong;
if (howlong == 0)
return (ARMul_DONE);
else
return (ARMul_BUSY);
}
else if (type == ARMul_BUSY)
{
if (ARMul_Time (state) >= finish)
return (ARMul_DONE);
else
return (ARMul_BUSY);
}
return (ARMul_DONE);
case 1:
if (howlong == 0)
ARMul_Abort (state, ARMul_FIQV);
else
ARMul_ScheduleEvent (state, howlong, DoAFIQ);
return (ARMul_DONE);
case 2:
if (howlong == 0)
ARMul_Abort (state, ARMul_IRQV);
else
ARMul_ScheduleEvent (state, howlong, DoAIRQ);
return (ARMul_DONE);
case 3:
state->NfiqSig = HIGH;
state->Exception--;
return (ARMul_DONE);
case 4:
state->NirqSig = HIGH;
state->Exception--;
return (ARMul_DONE);
case 5:
ValReg[BITS (0, 3)] = ARMul_Time (state);
return (ARMul_DONE);
}
return (ARMul_CANT);
}
/***************************************************************************\
* Install co-processor instruction handlers in this routine *
\***************************************************************************/
unsigned
ARMul_CoProInit (ARMul_State * state)
{
register unsigned i;
for (i = 0; i < 16; i++) /* initialise tham all first */
ARMul_CoProDetach (state, i);
/* Install CoPro Instruction handlers here
The format is
ARMul_CoProAttach(state, CP Number, Init routine, Exit routine
LDC routine, STC routine, MRC routine, MCR routine,
CDP routine, Read Reg routine, Write Reg routine) ;
*/
ARMul_CoProAttach (state, 4, NULL, NULL,
ValLDC, ValSTC, ValMRC, ValMCR, ValCDP, NULL, NULL);
ARMul_CoProAttach (state, 5, NULL, NULL,
NULL, NULL, ValMRC, ValMCR, IntCDP, NULL, NULL);
ARMul_CoProAttach (state, 15, MMUInit, NULL,
NULL, NULL, MMUMRC, MMUMCR, NULL, MMURead, MMUWrite);
/* No handlers below here */
for (i = 0; i < 16; i++) /* Call all the initialisation routines */
if (state->CPInit[i])
(state->CPInit[i]) (state);
return (TRUE);
}
/***************************************************************************\
* Install co-processor finalisation routines in this routine *
\***************************************************************************/
void
ARMul_CoProExit (ARMul_State * state)
{
register unsigned i;
for (i = 0; i < 16; i++)
if (state->CPExit[i])
(state->CPExit[i]) (state);
for (i = 0; i < 16; i++) /* Detach all handlers */
ARMul_CoProDetach (state, i);
}
/***************************************************************************\
* Routines to hook Co-processors into ARMulator *
\***************************************************************************/
void
ARMul_CoProAttach (ARMul_State * state, unsigned number,
ARMul_CPInits * init, ARMul_CPExits * exit,
ARMul_LDCs * ldc, ARMul_STCs * stc,
ARMul_MRCs * mrc, ARMul_MCRs * mcr, ARMul_CDPs * cdp,
ARMul_CPReads * read, ARMul_CPWrites * write)
{
if (init != NULL)
state->CPInit[number] = init;
if (exit != NULL)
state->CPExit[number] = exit;
if (ldc != NULL)
state->LDC[number] = ldc;
if (stc != NULL)
state->STC[number] = stc;
if (mrc != NULL)
state->MRC[number] = mrc;
if (mcr != NULL)
state->MCR[number] = mcr;
if (cdp != NULL)
state->CDP[number] = cdp;
if (read != NULL)
state->CPRead[number] = read;
if (write != NULL)
state->CPWrite[number] = write;
}
void
ARMul_CoProDetach (ARMul_State * state, unsigned number)
{
ARMul_CoProAttach (state, number, NULL, NULL,
NoCoPro4R, NoCoPro4W, NoCoPro4W, NoCoPro4R,
NoCoPro3R, NULL, NULL);
state->CPInit[number] = NULL;
state->CPExit[number] = NULL;
state->CPRead[number] = NULL;
state->CPWrite[number] = NULL;
}
/***************************************************************************\
* There is no CoPro around, so Undefined Instruction trap *
\***************************************************************************/
static unsigned
NoCoPro3R (ARMul_State * state ATTRIBUTE_UNUSED,
unsigned a ATTRIBUTE_UNUSED,
ARMword b ATTRIBUTE_UNUSED)
{
return (ARMul_CANT);
}
static unsigned
NoCoPro4R (
ARMul_State * state ATTRIBUTE_UNUSED,
unsigned a ATTRIBUTE_UNUSED,
ARMword b ATTRIBUTE_UNUSED,
ARMword c ATTRIBUTE_UNUSED)
{
return (ARMul_CANT);
}
static unsigned
NoCoPro4W (
ARMul_State * state ATTRIBUTE_UNUSED,
unsigned a ATTRIBUTE_UNUSED,
ARMword b ATTRIBUTE_UNUSED,
ARMword * c ATTRIBUTE_UNUSED)
{
return (ARMul_CANT);
}