mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:51:15 +08:00
3450 lines
88 KiB
C
3450 lines
88 KiB
C
/*> interp.c <*/
|
|
/* Simulator for the MIPS architecture.
|
|
|
|
This file is part of the MIPS sim
|
|
|
|
THIS SOFTWARE IS NOT COPYRIGHTED
|
|
|
|
Cygnus offers the following for use in the public domain. Cygnus
|
|
makes no warranty with regard to the software or it's performance
|
|
and the user accepts the software "AS IS" with all faults.
|
|
|
|
CYGNUS DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO
|
|
THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
$Revision$
|
|
$Date$
|
|
|
|
NOTEs:
|
|
|
|
The IDT monitor (found on the VR4300 board), seems to lie about
|
|
register contents. It seems to treat the registers as sign-extended
|
|
32-bit values. This cause *REAL* problems when single-stepping 64-bit
|
|
code on the hardware.
|
|
|
|
*/
|
|
|
|
/* The TRACE manifests enable the provision of extra features. If they
|
|
are not defined then a simpler (quicker) simulator is constructed
|
|
without the required run-time checks, etc. */
|
|
#if 1 /* 0 to allow user build selection, 1 to force inclusion */
|
|
#define TRACE (1)
|
|
#endif
|
|
|
|
#include "bfd.h"
|
|
#include "sim-main.h"
|
|
#include "sim-utils.h"
|
|
#include "sim-options.h"
|
|
#include "sim-assert.h"
|
|
#include "sim-hw.h"
|
|
|
|
#include "itable.h"
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <ansidecl.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#else
|
|
#ifdef HAVE_STRINGS_H
|
|
#include <strings.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include "getopt.h"
|
|
#include "libiberty.h"
|
|
#include "bfd.h"
|
|
#include "callback.h" /* GDB simulator callback interface */
|
|
#include "remote-sim.h" /* GDB simulator interface */
|
|
|
|
#include "sysdep.h"
|
|
|
|
#ifndef PARAMS
|
|
#define PARAMS(x)
|
|
#endif
|
|
|
|
char* pr_addr PARAMS ((SIM_ADDR addr));
|
|
char* pr_uword64 PARAMS ((uword64 addr));
|
|
|
|
|
|
/* Within interp.c we refer to the sim_state and sim_cpu directly. */
|
|
#define CPU cpu
|
|
#define SD sd
|
|
|
|
|
|
/* The following reserved instruction value is used when a simulator
|
|
trap is required. NOTE: Care must be taken, since this value may be
|
|
used in later revisions of the MIPS ISA. */
|
|
|
|
#define RSVD_INSTRUCTION (0x00000005)
|
|
#define RSVD_INSTRUCTION_MASK (0xFC00003F)
|
|
|
|
#define RSVD_INSTRUCTION_ARG_SHIFT 6
|
|
#define RSVD_INSTRUCTION_ARG_MASK 0xFFFFF
|
|
|
|
|
|
/* Bits in the Debug register */
|
|
#define Debug_DBD 0x80000000 /* Debug Branch Delay */
|
|
#define Debug_DM 0x40000000 /* Debug Mode */
|
|
#define Debug_DBp 0x00000002 /* Debug Breakpoint indicator */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/*-- GDB simulator interface ------------------------------------------------*/
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void ColdReset PARAMS((SIM_DESC sd));
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
#define DELAYSLOT() {\
|
|
if (STATE & simDELAYSLOT)\
|
|
sim_io_eprintf(sd,"Delay slot already activated (branch in delay slot?)\n");\
|
|
STATE |= simDELAYSLOT;\
|
|
}
|
|
|
|
#define JALDELAYSLOT() {\
|
|
DELAYSLOT ();\
|
|
STATE |= simJALDELAYSLOT;\
|
|
}
|
|
|
|
#define NULLIFY() {\
|
|
STATE &= ~simDELAYSLOT;\
|
|
STATE |= simSKIPNEXT;\
|
|
}
|
|
|
|
#define CANCELDELAYSLOT() {\
|
|
DSSTATE = 0;\
|
|
STATE &= ~(simDELAYSLOT | simJALDELAYSLOT);\
|
|
}
|
|
|
|
#define INDELAYSLOT() ((STATE & simDELAYSLOT) != 0)
|
|
#define INJALDELAYSLOT() ((STATE & simJALDELAYSLOT) != 0)
|
|
|
|
/* Note that the monitor code essentially assumes this layout of memory.
|
|
If you change these, change the monitor code, too. */
|
|
#define K0BASE (0x80000000)
|
|
#define K0SIZE (0x20000000)
|
|
#define K1BASE (0xA0000000)
|
|
#define K1SIZE (0x20000000)
|
|
|
|
/* Simple run-time monitor support.
|
|
|
|
We emulate the monitor by placing magic reserved instructions at
|
|
the monitor's entry points; when we hit these instructions, instead
|
|
of raising an exception (as we would normally), we look at the
|
|
instruction and perform the appropriate monitory operation.
|
|
|
|
`*_monitor_base' are the physical addresses at which the corresponding
|
|
monitor vectors are located. `0' means none. By default,
|
|
install all three.
|
|
The RSVD_INSTRUCTION... macros specify the magic instructions we
|
|
use at the monitor entry points. */
|
|
static int firmware_option_p = 0;
|
|
static SIM_ADDR idt_monitor_base = 0xBFC00000;
|
|
static SIM_ADDR pmon_monitor_base = 0xBFC00500;
|
|
static SIM_ADDR lsipmon_monitor_base = 0xBFC00200;
|
|
|
|
static SIM_RC sim_firmware_command (SIM_DESC sd, char* arg);
|
|
|
|
|
|
#define MEM_SIZE (2 << 20)
|
|
|
|
|
|
#if defined(TRACE)
|
|
static char *tracefile = "trace.din"; /* default filename for trace log */
|
|
FILE *tracefh = NULL;
|
|
static void open_trace PARAMS((SIM_DESC sd));
|
|
#endif /* TRACE */
|
|
|
|
static const char * get_insn_name (sim_cpu *, int);
|
|
|
|
/* simulation target board. NULL=canonical */
|
|
static char* board = NULL;
|
|
|
|
|
|
static DECLARE_OPTION_HANDLER (mips_option_handler);
|
|
|
|
enum {
|
|
OPTION_DINERO_TRACE = OPTION_START,
|
|
OPTION_DINERO_FILE,
|
|
OPTION_FIRMWARE,
|
|
OPTION_BOARD
|
|
};
|
|
|
|
|
|
static SIM_RC
|
|
mips_option_handler (sd, cpu, opt, arg, is_command)
|
|
SIM_DESC sd;
|
|
sim_cpu *cpu;
|
|
int opt;
|
|
char *arg;
|
|
int is_command;
|
|
{
|
|
int cpu_nr;
|
|
switch (opt)
|
|
{
|
|
case OPTION_DINERO_TRACE: /* ??? */
|
|
#if defined(TRACE)
|
|
/* Eventually the simTRACE flag could be treated as a toggle, to
|
|
allow external control of the program points being traced
|
|
(i.e. only from main onwards, excluding the run-time setup,
|
|
etc.). */
|
|
for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; cpu_nr++)
|
|
{
|
|
sim_cpu *cpu = STATE_CPU (sd, cpu_nr);
|
|
if (arg == NULL)
|
|
STATE |= simTRACE;
|
|
else if (strcmp (arg, "yes") == 0)
|
|
STATE |= simTRACE;
|
|
else if (strcmp (arg, "no") == 0)
|
|
STATE &= ~simTRACE;
|
|
else if (strcmp (arg, "on") == 0)
|
|
STATE |= simTRACE;
|
|
else if (strcmp (arg, "off") == 0)
|
|
STATE &= ~simTRACE;
|
|
else
|
|
{
|
|
fprintf (stderr, "Unrecognized dinero-trace option `%s'\n", arg);
|
|
return SIM_RC_FAIL;
|
|
}
|
|
}
|
|
return SIM_RC_OK;
|
|
#else /* !TRACE */
|
|
fprintf(stderr,"\
|
|
Simulator constructed without dinero tracing support (for performance).\n\
|
|
Re-compile simulator with \"-DTRACE\" to enable this option.\n");
|
|
return SIM_RC_FAIL;
|
|
#endif /* !TRACE */
|
|
|
|
case OPTION_DINERO_FILE:
|
|
#if defined(TRACE)
|
|
if (optarg != NULL) {
|
|
char *tmp;
|
|
tmp = (char *)malloc(strlen(optarg) + 1);
|
|
if (tmp == NULL)
|
|
{
|
|
sim_io_printf(sd,"Failed to allocate buffer for tracefile name \"%s\"\n",optarg);
|
|
return SIM_RC_FAIL;
|
|
}
|
|
else {
|
|
strcpy(tmp,optarg);
|
|
tracefile = tmp;
|
|
sim_io_printf(sd,"Placing trace information into file \"%s\"\n",tracefile);
|
|
}
|
|
}
|
|
#endif /* TRACE */
|
|
return SIM_RC_OK;
|
|
|
|
case OPTION_FIRMWARE:
|
|
return sim_firmware_command (sd, arg);
|
|
|
|
case OPTION_BOARD:
|
|
{
|
|
if (arg)
|
|
{
|
|
board = zalloc(strlen(arg) + 1);
|
|
strcpy(board, arg);
|
|
}
|
|
return SIM_RC_OK;
|
|
}
|
|
}
|
|
|
|
return SIM_RC_OK;
|
|
}
|
|
|
|
|
|
static const OPTION mips_options[] =
|
|
{
|
|
{ {"dinero-trace", optional_argument, NULL, OPTION_DINERO_TRACE},
|
|
'\0', "on|off", "Enable dinero tracing",
|
|
mips_option_handler },
|
|
{ {"dinero-file", required_argument, NULL, OPTION_DINERO_FILE},
|
|
'\0', "FILE", "Write dinero trace to FILE",
|
|
mips_option_handler },
|
|
{ {"firmware", required_argument, NULL, OPTION_FIRMWARE},
|
|
'\0', "[idt|pmon|lsipmon|none][@ADDRESS]", "Emulate ROM monitor",
|
|
mips_option_handler },
|
|
{ {"board", required_argument, NULL, OPTION_BOARD},
|
|
'\0', "none" /* rely on compile-time string concatenation for other options */
|
|
|
|
#define BOARD_JMR3904 "jmr3904"
|
|
"|" BOARD_JMR3904
|
|
#define BOARD_JMR3904_PAL "jmr3904pal"
|
|
"|" BOARD_JMR3904_PAL
|
|
#define BOARD_JMR3904_DEBUG "jmr3904debug"
|
|
"|" BOARD_JMR3904_DEBUG
|
|
#define BOARD_BSP "bsp"
|
|
"|" BOARD_BSP
|
|
|
|
, "Customize simulation for a particular board.", mips_option_handler },
|
|
|
|
{ {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL }
|
|
};
|
|
|
|
|
|
int interrupt_pending;
|
|
|
|
void
|
|
interrupt_event (SIM_DESC sd, void *data)
|
|
{
|
|
sim_cpu *cpu = STATE_CPU (sd, 0); /* FIXME */
|
|
address_word cia = CIA_GET (cpu);
|
|
if (SR & status_IE)
|
|
{
|
|
interrupt_pending = 0;
|
|
SignalExceptionInterrupt (1); /* interrupt "1" */
|
|
}
|
|
else if (!interrupt_pending)
|
|
sim_events_schedule (sd, 1, interrupt_event, data);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/*-- Device registration hook -----------------------------------------------*/
|
|
/*---------------------------------------------------------------------------*/
|
|
static void device_init(SIM_DESC sd) {
|
|
#ifdef DEVICE_INIT
|
|
extern void register_devices(SIM_DESC);
|
|
register_devices(sd);
|
|
#endif
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/*-- GDB simulator interface ------------------------------------------------*/
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
SIM_DESC
|
|
sim_open (kind, cb, abfd, argv)
|
|
SIM_OPEN_KIND kind;
|
|
host_callback *cb;
|
|
struct _bfd *abfd;
|
|
char **argv;
|
|
{
|
|
SIM_DESC sd = sim_state_alloc (kind, cb);
|
|
sim_cpu *cpu = STATE_CPU (sd, 0); /* FIXME */
|
|
|
|
SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
|
|
|
|
/* FIXME: watchpoints code shouldn't need this */
|
|
STATE_WATCHPOINTS (sd)->pc = &(PC);
|
|
STATE_WATCHPOINTS (sd)->sizeof_pc = sizeof (PC);
|
|
STATE_WATCHPOINTS (sd)->interrupt_handler = interrupt_event;
|
|
|
|
/* Initialize the mechanism for doing insn profiling. */
|
|
CPU_INSN_NAME (cpu) = get_insn_name;
|
|
CPU_MAX_INSNS (cpu) = nr_itable_entries;
|
|
|
|
STATE = 0;
|
|
|
|
if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
|
|
return 0;
|
|
sim_add_option_table (sd, NULL, mips_options);
|
|
|
|
|
|
/* getopt will print the error message so we just have to exit if this fails.
|
|
FIXME: Hmmm... in the case of gdb we need getopt to call
|
|
print_filtered. */
|
|
if (sim_parse_args (sd, argv) != SIM_RC_OK)
|
|
{
|
|
/* Uninstall the modules to avoid memory leaks,
|
|
file descriptor leaks, etc. */
|
|
sim_module_uninstall (sd);
|
|
return 0;
|
|
}
|
|
|
|
/* handle board-specific memory maps */
|
|
if (board == NULL)
|
|
{
|
|
/* Allocate core managed memory */
|
|
|
|
|
|
/* For compatibility with the old code - under this (at level one)
|
|
are the kernel spaces K0 & K1. Both of these map to a single
|
|
smaller sub region */
|
|
sim_do_command(sd," memory region 0x7fff8000,0x8000") ; /* MTZ- 32 k stack */
|
|
sim_do_commandf (sd, "memory alias 0x%lx@1,0x%lx%%0x%lx,0x%0x",
|
|
K1BASE, K0SIZE,
|
|
MEM_SIZE, /* actual size */
|
|
K0BASE);
|
|
|
|
device_init(sd);
|
|
}
|
|
else if (board != NULL
|
|
&& (strcmp(board, BOARD_BSP) == 0))
|
|
{
|
|
int i;
|
|
|
|
STATE_ENVIRONMENT (sd) = OPERATING_ENVIRONMENT;
|
|
|
|
/* ROM: 0x9FC0_0000 - 0x9FFF_FFFF and 0xBFC0_0000 - 0xBFFF_FFFF */
|
|
sim_do_commandf (sd, "memory alias 0x%lx@1,0x%lx,0x%0x",
|
|
0x9FC00000,
|
|
4 * 1024 * 1024, /* 4 MB */
|
|
0xBFC00000);
|
|
|
|
/* SRAM: 0x8000_0000 - 0x803F_FFFF and 0xA000_0000 - 0xA03F_FFFF */
|
|
sim_do_commandf (sd, "memory alias 0x%lx@1,0x%lx,0x%0x",
|
|
0x80000000,
|
|
4 * 1024 * 1024, /* 4 MB */
|
|
0xA0000000);
|
|
|
|
/* DRAM: 0x8800_0000 - 0x89FF_FFFF and 0xA800_0000 - 0xA9FF_FFFF */
|
|
for (i=0; i<8; i++) /* 32 MB total */
|
|
{
|
|
unsigned size = 4 * 1024 * 1024; /* 4 MB */
|
|
sim_do_commandf (sd, "memory alias 0x%lx@1,0x%lx,0x%0x",
|
|
0x88000000 + (i * size),
|
|
size,
|
|
0xA8000000 + (i * size));
|
|
}
|
|
}
|
|
#if (WITH_HW)
|
|
else if (board != NULL
|
|
&& (strcmp(board, BOARD_JMR3904) == 0 ||
|
|
strcmp(board, BOARD_JMR3904_PAL) == 0 ||
|
|
strcmp(board, BOARD_JMR3904_DEBUG) == 0))
|
|
{
|
|
/* match VIRTUAL memory layout of JMR-TX3904 board */
|
|
int i;
|
|
|
|
/* --- disable monitor unless forced on by user --- */
|
|
|
|
if (! firmware_option_p)
|
|
{
|
|
idt_monitor_base = 0;
|
|
pmon_monitor_base = 0;
|
|
lsipmon_monitor_base = 0;
|
|
}
|
|
|
|
/* --- environment --- */
|
|
|
|
STATE_ENVIRONMENT (sd) = OPERATING_ENVIRONMENT;
|
|
|
|
/* --- memory --- */
|
|
|
|
/* ROM: 0x9FC0_0000 - 0x9FFF_FFFF and 0xBFC0_0000 - 0xBFFF_FFFF */
|
|
sim_do_commandf (sd, "memory alias 0x%lx@1,0x%lx,0x%0x",
|
|
0x9FC00000,
|
|
4 * 1024 * 1024, /* 4 MB */
|
|
0xBFC00000);
|
|
|
|
/* SRAM: 0x8000_0000 - 0x803F_FFFF and 0xA000_0000 - 0xA03F_FFFF */
|
|
sim_do_commandf (sd, "memory alias 0x%lx@1,0x%lx,0x%0x",
|
|
0x80000000,
|
|
4 * 1024 * 1024, /* 4 MB */
|
|
0xA0000000);
|
|
|
|
/* DRAM: 0x8800_0000 - 0x89FF_FFFF and 0xA800_0000 - 0xA9FF_FFFF */
|
|
for (i=0; i<8; i++) /* 32 MB total */
|
|
{
|
|
unsigned size = 4 * 1024 * 1024; /* 4 MB */
|
|
sim_do_commandf (sd, "memory alias 0x%lx@1,0x%lx,0x%0x",
|
|
0x88000000 + (i * size),
|
|
size,
|
|
0xA8000000 + (i * size));
|
|
}
|
|
|
|
/* Dummy memory regions for unsimulated devices */
|
|
|
|
sim_do_commandf (sd, "memory alias 0x%lx@1,0x%lx", 0xFFFFE010, 0x00c); /* EBIF */
|
|
sim_do_commandf (sd, "memory alias 0x%lx@1,0x%lx", 0xFFFF9000, 0x200); /* EBIF */
|
|
sim_do_commandf (sd, "memory alias 0x%lx@1,0x%lx", 0xFFFFF500, 0x300); /* PIO */
|
|
|
|
/* --- simulated devices --- */
|
|
sim_hw_parse (sd, "/tx3904irc@0xffffc000/reg 0xffffc000 0x20");
|
|
sim_hw_parse (sd, "/tx3904cpu");
|
|
sim_hw_parse (sd, "/tx3904tmr@0xfffff000/reg 0xfffff000 0x100");
|
|
sim_hw_parse (sd, "/tx3904tmr@0xfffff100/reg 0xfffff100 0x100");
|
|
sim_hw_parse (sd, "/tx3904tmr@0xfffff200/reg 0xfffff200 0x100");
|
|
sim_hw_parse (sd, "/tx3904sio@0xfffff300/reg 0xfffff300 0x100");
|
|
{
|
|
/* FIXME: poking at dv-sockser internals, use tcp backend if
|
|
--sockser_addr option was given.*/
|
|
extern char* sockser_addr;
|
|
if(sockser_addr == NULL)
|
|
sim_hw_parse (sd, "/tx3904sio@0xfffff300/backend stdio");
|
|
else
|
|
sim_hw_parse (sd, "/tx3904sio@0xfffff300/backend tcp");
|
|
}
|
|
sim_hw_parse (sd, "/tx3904sio@0xfffff400/reg 0xfffff400 0x100");
|
|
sim_hw_parse (sd, "/tx3904sio@0xfffff400/backend stdio");
|
|
|
|
/* -- device connections --- */
|
|
sim_hw_parse (sd, "/tx3904irc > ip level /tx3904cpu");
|
|
sim_hw_parse (sd, "/tx3904tmr@0xfffff000 > int tmr0 /tx3904irc");
|
|
sim_hw_parse (sd, "/tx3904tmr@0xfffff100 > int tmr1 /tx3904irc");
|
|
sim_hw_parse (sd, "/tx3904tmr@0xfffff200 > int tmr2 /tx3904irc");
|
|
sim_hw_parse (sd, "/tx3904sio@0xfffff300 > int sio0 /tx3904irc");
|
|
sim_hw_parse (sd, "/tx3904sio@0xfffff400 > int sio1 /tx3904irc");
|
|
|
|
/* add PAL timer & I/O module */
|
|
if(! strcmp(board, BOARD_JMR3904_PAL))
|
|
{
|
|
/* the device */
|
|
sim_hw_parse (sd, "/pal@0xffff0000");
|
|
sim_hw_parse (sd, "/pal@0xffff0000/reg 0xffff0000 64");
|
|
|
|
/* wire up interrupt ports to irc */
|
|
sim_hw_parse (sd, "/pal@0x31000000 > countdown tmr0 /tx3904irc");
|
|
sim_hw_parse (sd, "/pal@0x31000000 > timer tmr1 /tx3904irc");
|
|
sim_hw_parse (sd, "/pal@0x31000000 > int int0 /tx3904irc");
|
|
}
|
|
|
|
if(! strcmp(board, BOARD_JMR3904_DEBUG))
|
|
{
|
|
/* -- DEBUG: glue interrupt generators --- */
|
|
sim_hw_parse (sd, "/glue@0xffff0000/reg 0xffff0000 0x50");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int0 int0 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int1 int1 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int2 int2 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int3 int3 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int4 int4 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int5 int5 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int6 int6 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int7 int7 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int8 dmac0 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int9 dmac1 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int10 dmac2 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int11 dmac3 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int12 sio0 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int13 sio1 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int14 tmr0 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int15 tmr1 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int16 tmr2 /tx3904irc");
|
|
sim_hw_parse (sd, "/glue@0xffff0000 > int17 nmi /tx3904cpu");
|
|
}
|
|
|
|
device_init(sd);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* check for/establish the a reference program image */
|
|
if (sim_analyze_program (sd,
|
|
(STATE_PROG_ARGV (sd) != NULL
|
|
? *STATE_PROG_ARGV (sd)
|
|
: NULL),
|
|
abfd) != SIM_RC_OK)
|
|
{
|
|
sim_module_uninstall (sd);
|
|
return 0;
|
|
}
|
|
|
|
/* Configure/verify the target byte order and other runtime
|
|
configuration options */
|
|
if (sim_config (sd) != SIM_RC_OK)
|
|
{
|
|
sim_module_uninstall (sd);
|
|
return 0;
|
|
}
|
|
|
|
if (sim_post_argv_init (sd) != SIM_RC_OK)
|
|
{
|
|
/* Uninstall the modules to avoid memory leaks,
|
|
file descriptor leaks, etc. */
|
|
sim_module_uninstall (sd);
|
|
return 0;
|
|
}
|
|
|
|
/* verify assumptions the simulator made about the host type system.
|
|
This macro does not return if there is a problem */
|
|
SIM_ASSERT (sizeof(int) == (4 * sizeof(char)));
|
|
SIM_ASSERT (sizeof(word64) == (8 * sizeof(char)));
|
|
|
|
/* This is NASTY, in that we are assuming the size of specific
|
|
registers: */
|
|
{
|
|
int rn;
|
|
for (rn = 0; (rn < (LAST_EMBED_REGNUM + 1)); rn++)
|
|
{
|
|
if (rn < 32)
|
|
cpu->register_widths[rn] = WITH_TARGET_WORD_BITSIZE;
|
|
else if ((rn >= FGRIDX) && (rn < (FGRIDX + NR_FGR)))
|
|
cpu->register_widths[rn] = WITH_TARGET_FLOATING_POINT_BITSIZE;
|
|
else if ((rn >= 33) && (rn <= 37))
|
|
cpu->register_widths[rn] = WITH_TARGET_WORD_BITSIZE;
|
|
else if ((rn == SRIDX)
|
|
|| (rn == FCR0IDX)
|
|
|| (rn == FCR31IDX)
|
|
|| ((rn >= 72) && (rn <= 89)))
|
|
cpu->register_widths[rn] = 32;
|
|
else
|
|
cpu->register_widths[rn] = 0;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
#if defined(TRACE)
|
|
if (STATE & simTRACE)
|
|
open_trace(sd);
|
|
#endif /* TRACE */
|
|
|
|
/*
|
|
sim_io_eprintf (sd, "idt@%x pmon@%x lsipmon@%x\n",
|
|
idt_monitor_base,
|
|
pmon_monitor_base,
|
|
lsipmon_monitor_base);
|
|
*/
|
|
|
|
/* Write the monitor trap address handlers into the monitor (eeprom)
|
|
address space. This can only be done once the target endianness
|
|
has been determined. */
|
|
if (idt_monitor_base != 0)
|
|
{
|
|
unsigned loop;
|
|
unsigned idt_monitor_size = 1 << 11;
|
|
|
|
/* the default monitor region */
|
|
sim_do_commandf (sd, "memory region 0x%x,0x%x",
|
|
idt_monitor_base, idt_monitor_size);
|
|
|
|
/* Entry into the IDT monitor is via fixed address vectors, and
|
|
not using machine instructions. To avoid clashing with use of
|
|
the MIPS TRAP system, we place our own (simulator specific)
|
|
"undefined" instructions into the relevant vector slots. */
|
|
for (loop = 0; (loop < idt_monitor_size); loop += 4)
|
|
{
|
|
address_word vaddr = (idt_monitor_base + loop);
|
|
unsigned32 insn = (RSVD_INSTRUCTION |
|
|
(((loop >> 2) & RSVD_INSTRUCTION_ARG_MASK)
|
|
<< RSVD_INSTRUCTION_ARG_SHIFT));
|
|
H2T (insn);
|
|
sim_write (sd, vaddr, (char *)&insn, sizeof (insn));
|
|
}
|
|
}
|
|
|
|
if ((pmon_monitor_base != 0) || (lsipmon_monitor_base != 0))
|
|
{
|
|
/* The PMON monitor uses the same address space, but rather than
|
|
branching into it the address of a routine is loaded. We can
|
|
cheat for the moment, and direct the PMON routine to IDT style
|
|
instructions within the monitor space. This relies on the IDT
|
|
monitor not using the locations from 0xBFC00500 onwards as its
|
|
entry points.*/
|
|
unsigned loop;
|
|
for (loop = 0; (loop < 24); loop++)
|
|
{
|
|
unsigned32 value = ((0x500 - 8) / 8); /* default UNDEFINED reason code */
|
|
switch (loop)
|
|
{
|
|
case 0: /* read */
|
|
value = 7;
|
|
break;
|
|
case 1: /* write */
|
|
value = 8;
|
|
break;
|
|
case 2: /* open */
|
|
value = 6;
|
|
break;
|
|
case 3: /* close */
|
|
value = 10;
|
|
break;
|
|
case 5: /* printf */
|
|
value = ((0x500 - 16) / 8); /* not an IDT reason code */
|
|
break;
|
|
case 8: /* cliexit */
|
|
value = 17;
|
|
break;
|
|
case 11: /* flush_cache */
|
|
value = 28;
|
|
break;
|
|
}
|
|
|
|
SIM_ASSERT (idt_monitor_base != 0);
|
|
value = ((unsigned int) idt_monitor_base + (value * 8));
|
|
H2T (value);
|
|
|
|
if (pmon_monitor_base != 0)
|
|
{
|
|
address_word vaddr = (pmon_monitor_base + (loop * 4));
|
|
sim_write (sd, vaddr, (char *)&value, sizeof (value));
|
|
}
|
|
|
|
if (lsipmon_monitor_base != 0)
|
|
{
|
|
address_word vaddr = (lsipmon_monitor_base + (loop * 4));
|
|
sim_write (sd, vaddr, (char *)&value, sizeof (value));
|
|
}
|
|
}
|
|
|
|
/* Write an abort sequence into the TRAP (common) exception vector
|
|
addresses. This is to catch code executing a TRAP (et.al.)
|
|
instruction without installing a trap handler. */
|
|
if ((idt_monitor_base != 0) ||
|
|
(pmon_monitor_base != 0) ||
|
|
(lsipmon_monitor_base != 0))
|
|
{
|
|
unsigned32 halt[2] = { 0x2404002f /* addiu r4, r0, 47 */,
|
|
HALT_INSTRUCTION /* BREAK */ };
|
|
H2T (halt[0]);
|
|
H2T (halt[1]);
|
|
sim_write (sd, 0x80000000, (char *) halt, sizeof (halt));
|
|
sim_write (sd, 0x80000180, (char *) halt, sizeof (halt));
|
|
sim_write (sd, 0x80000200, (char *) halt, sizeof (halt));
|
|
/* XXX: Write here unconditionally? */
|
|
sim_write (sd, 0xBFC00200, (char *) halt, sizeof (halt));
|
|
sim_write (sd, 0xBFC00380, (char *) halt, sizeof (halt));
|
|
sim_write (sd, 0xBFC00400, (char *) halt, sizeof (halt));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return sd;
|
|
}
|
|
|
|
#if defined(TRACE)
|
|
static void
|
|
open_trace(sd)
|
|
SIM_DESC sd;
|
|
{
|
|
tracefh = fopen(tracefile,"wb+");
|
|
if (tracefh == NULL)
|
|
{
|
|
sim_io_eprintf(sd,"Failed to create file \"%s\", writing trace information to stderr.\n",tracefile);
|
|
tracefh = stderr;
|
|
}
|
|
}
|
|
#endif /* TRACE */
|
|
|
|
/* Return name of an insn, used by insn profiling. */
|
|
static const char *
|
|
get_insn_name (sim_cpu *cpu, int i)
|
|
{
|
|
return itable[i].name;
|
|
}
|
|
|
|
void
|
|
sim_close (sd, quitting)
|
|
SIM_DESC sd;
|
|
int quitting;
|
|
{
|
|
#ifdef DEBUG
|
|
printf("DBG: sim_close: entered (quitting = %d)\n",quitting);
|
|
#endif
|
|
|
|
|
|
/* "quitting" is non-zero if we cannot hang on errors */
|
|
|
|
/* shut down modules */
|
|
sim_module_uninstall (sd);
|
|
|
|
/* Ensure that any resources allocated through the callback
|
|
mechanism are released: */
|
|
sim_io_shutdown (sd);
|
|
|
|
#if defined(TRACE)
|
|
if (tracefh != NULL && tracefh != stderr)
|
|
fclose(tracefh);
|
|
tracefh = NULL;
|
|
#endif /* TRACE */
|
|
|
|
/* FIXME - free SD */
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
int
|
|
sim_write (sd,addr,buffer,size)
|
|
SIM_DESC sd;
|
|
SIM_ADDR addr;
|
|
unsigned char *buffer;
|
|
int size;
|
|
{
|
|
int index;
|
|
sim_cpu *cpu = STATE_CPU (sd, 0); /* FIXME */
|
|
|
|
/* Return the number of bytes written, or zero if error. */
|
|
#ifdef DEBUG
|
|
sim_io_printf(sd,"sim_write(0x%s,buffer,%d);\n",pr_addr(addr),size);
|
|
#endif
|
|
|
|
/* We use raw read and write routines, since we do not want to count
|
|
the GDB memory accesses in our statistics gathering. */
|
|
|
|
for (index = 0; index < size; index++)
|
|
{
|
|
address_word vaddr = (address_word)addr + index;
|
|
address_word paddr;
|
|
int cca;
|
|
if (!address_translation (SD, CPU, NULL_CIA, vaddr, isDATA, isSTORE, &paddr, &cca, isRAW))
|
|
break;
|
|
if (sim_core_write_buffer (SD, CPU, read_map, buffer + index, paddr, 1) != 1)
|
|
break;
|
|
}
|
|
|
|
return(index);
|
|
}
|
|
|
|
int
|
|
sim_read (sd,addr,buffer,size)
|
|
SIM_DESC sd;
|
|
SIM_ADDR addr;
|
|
unsigned char *buffer;
|
|
int size;
|
|
{
|
|
int index;
|
|
sim_cpu *cpu = STATE_CPU (sd, 0); /* FIXME */
|
|
|
|
/* Return the number of bytes read, or zero if error. */
|
|
#ifdef DEBUG
|
|
sim_io_printf(sd,"sim_read(0x%s,buffer,%d);\n",pr_addr(addr),size);
|
|
#endif /* DEBUG */
|
|
|
|
for (index = 0; (index < size); index++)
|
|
{
|
|
address_word vaddr = (address_word)addr + index;
|
|
address_word paddr;
|
|
int cca;
|
|
if (!address_translation (SD, CPU, NULL_CIA, vaddr, isDATA, isLOAD, &paddr, &cca, isRAW))
|
|
break;
|
|
if (sim_core_read_buffer (SD, CPU, read_map, buffer + index, paddr, 1) != 1)
|
|
break;
|
|
}
|
|
|
|
return(index);
|
|
}
|
|
|
|
int
|
|
sim_store_register (sd,rn,memory,length)
|
|
SIM_DESC sd;
|
|
int rn;
|
|
unsigned char *memory;
|
|
int length;
|
|
{
|
|
sim_cpu *cpu = STATE_CPU (sd, 0); /* FIXME */
|
|
/* NOTE: gdb (the client) stores registers in target byte order
|
|
while the simulator uses host byte order */
|
|
#ifdef DEBUG
|
|
sim_io_printf(sd,"sim_store_register(%d,*memory=0x%s);\n",rn,pr_addr(*((SIM_ADDR *)memory)));
|
|
#endif /* DEBUG */
|
|
|
|
/* Unfortunately this suffers from the same problem as the register
|
|
numbering one. We need to know what the width of each logical
|
|
register number is for the architecture being simulated. */
|
|
|
|
if (cpu->register_widths[rn] == 0)
|
|
{
|
|
sim_io_eprintf(sd,"Invalid register width for %d (register store ignored)\n",rn);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
if (rn >= FGRIDX && rn < FGRIDX + NR_FGR)
|
|
{
|
|
cpu->fpr_state[rn - FGRIDX] = fmt_uninterpreted;
|
|
if (cpu->register_widths[rn] == 32)
|
|
{
|
|
if (length == 8)
|
|
{
|
|
cpu->fgr[rn - FGRIDX] =
|
|
(unsigned32) T2H_8 (*(unsigned64*)memory);
|
|
return 8;
|
|
}
|
|
else
|
|
{
|
|
cpu->fgr[rn - FGRIDX] = T2H_4 (*(unsigned32*)memory);
|
|
return 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cpu->fgr[rn - FGRIDX] = T2H_8 (*(unsigned64*)memory);
|
|
return 8;
|
|
}
|
|
}
|
|
|
|
if (cpu->register_widths[rn] == 32)
|
|
{
|
|
if (length == 8)
|
|
{
|
|
cpu->registers[rn] =
|
|
(unsigned32) T2H_8 (*(unsigned64*)memory);
|
|
return 8;
|
|
}
|
|
else
|
|
{
|
|
cpu->registers[rn] = T2H_4 (*(unsigned32*)memory);
|
|
return 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cpu->registers[rn] = T2H_8 (*(unsigned64*)memory);
|
|
return 8;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sim_fetch_register (sd,rn,memory,length)
|
|
SIM_DESC sd;
|
|
int rn;
|
|
unsigned char *memory;
|
|
int length;
|
|
{
|
|
sim_cpu *cpu = STATE_CPU (sd, 0); /* FIXME */
|
|
/* NOTE: gdb (the client) stores registers in target byte order
|
|
while the simulator uses host byte order */
|
|
#ifdef DEBUG
|
|
#if 0 /* FIXME: doesn't compile */
|
|
sim_io_printf(sd,"sim_fetch_register(%d=0x%s,mem) : place simulator registers into memory\n",rn,pr_addr(registers[rn]));
|
|
#endif
|
|
#endif /* DEBUG */
|
|
|
|
if (cpu->register_widths[rn] == 0)
|
|
{
|
|
sim_io_eprintf (sd, "Invalid register width for %d (register fetch ignored)\n",rn);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* Any floating point register */
|
|
if (rn >= FGRIDX && rn < FGRIDX + NR_FGR)
|
|
{
|
|
if (cpu->register_widths[rn] == 32)
|
|
{
|
|
if (length == 8)
|
|
{
|
|
*(unsigned64*)memory =
|
|
H2T_8 ((unsigned32) (cpu->fgr[rn - FGRIDX]));
|
|
return 8;
|
|
}
|
|
else
|
|
{
|
|
*(unsigned32*)memory = H2T_4 (cpu->fgr[rn - FGRIDX]);
|
|
return 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*(unsigned64*)memory = H2T_8 (cpu->fgr[rn - FGRIDX]);
|
|
return 8;
|
|
}
|
|
}
|
|
|
|
if (cpu->register_widths[rn] == 32)
|
|
{
|
|
if (length == 8)
|
|
{
|
|
*(unsigned64*)memory =
|
|
H2T_8 ((unsigned32) (cpu->registers[rn]));
|
|
return 8;
|
|
}
|
|
else
|
|
{
|
|
*(unsigned32*)memory = H2T_4 ((unsigned32)(cpu->registers[rn]));
|
|
return 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*(unsigned64*)memory = H2T_8 ((unsigned64)(cpu->registers[rn]));
|
|
return 8;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
SIM_RC
|
|
sim_create_inferior (sd, abfd, argv,env)
|
|
SIM_DESC sd;
|
|
struct _bfd *abfd;
|
|
char **argv;
|
|
char **env;
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
#if 0 /* FIXME: doesn't compile */
|
|
printf("DBG: sim_create_inferior entered: start_address = 0x%s\n",
|
|
pr_addr(PC));
|
|
#endif
|
|
#endif /* DEBUG */
|
|
|
|
ColdReset(sd);
|
|
|
|
if (abfd != NULL)
|
|
{
|
|
/* override PC value set by ColdReset () */
|
|
int cpu_nr;
|
|
for (cpu_nr = 0; cpu_nr < sim_engine_nr_cpus (sd); cpu_nr++)
|
|
{
|
|
sim_cpu *cpu = STATE_CPU (sd, cpu_nr);
|
|
CIA_SET (cpu, (unsigned64) bfd_get_start_address (abfd));
|
|
}
|
|
}
|
|
|
|
#if 0 /* def DEBUG */
|
|
if (argv || env)
|
|
{
|
|
/* We should really place the argv slot values into the argument
|
|
registers, and onto the stack as required. However, this
|
|
assumes that we have a stack defined, which is not
|
|
necessarily true at the moment. */
|
|
char **cptr;
|
|
sim_io_printf(sd,"sim_create_inferior() : passed arguments ignored\n");
|
|
for (cptr = argv; (cptr && *cptr); cptr++)
|
|
printf("DBG: arg \"%s\"\n",*cptr);
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
return SIM_RC_OK;
|
|
}
|
|
|
|
void
|
|
sim_do_command (sd,cmd)
|
|
SIM_DESC sd;
|
|
char *cmd;
|
|
{
|
|
if (sim_args_command (sd, cmd) != SIM_RC_OK)
|
|
sim_io_printf (sd, "Error: \"%s\" is not a valid MIPS simulator command.\n",
|
|
cmd);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/*-- Private simulator support interface ------------------------------------*/
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* Read a null terminated string from memory, return in a buffer */
|
|
static char *
|
|
fetch_str (SIM_DESC sd,
|
|
address_word addr)
|
|
{
|
|
char *buf;
|
|
int nr = 0;
|
|
char null;
|
|
while (sim_read (sd, addr + nr, &null, 1) == 1 && null != 0)
|
|
nr++;
|
|
buf = NZALLOC (char, nr + 1);
|
|
sim_read (sd, addr, buf, nr);
|
|
return buf;
|
|
}
|
|
|
|
|
|
/* Implements the "sim firmware" command:
|
|
sim firmware NAME[@ADDRESS] --- emulate ROM monitor named NAME.
|
|
NAME can be idt, pmon, or lsipmon. If omitted, ADDRESS
|
|
defaults to the normal address for that monitor.
|
|
sim firmware none --- don't emulate any ROM monitor. Useful
|
|
if you need a clean address space. */
|
|
static SIM_RC
|
|
sim_firmware_command (SIM_DESC sd, char *arg)
|
|
{
|
|
int address_present = 0;
|
|
SIM_ADDR address;
|
|
|
|
/* Signal occurrence of this option. */
|
|
firmware_option_p = 1;
|
|
|
|
/* Parse out the address, if present. */
|
|
{
|
|
char *p = strchr (arg, '@');
|
|
if (p)
|
|
{
|
|
char *q;
|
|
address_present = 1;
|
|
p ++; /* skip over @ */
|
|
|
|
address = strtoul (p, &q, 0);
|
|
if (*q != '\0')
|
|
{
|
|
sim_io_printf (sd, "Invalid address given to the"
|
|
"`sim firmware NAME@ADDRESS' command: %s\n",
|
|
p);
|
|
return SIM_RC_FAIL;
|
|
}
|
|
}
|
|
else
|
|
address_present = 0;
|
|
}
|
|
|
|
if (! strncmp (arg, "idt", 3))
|
|
{
|
|
idt_monitor_base = address_present ? address : 0xBFC00000;
|
|
pmon_monitor_base = 0;
|
|
lsipmon_monitor_base = 0;
|
|
}
|
|
else if (! strncmp (arg, "pmon", 4))
|
|
{
|
|
/* pmon uses indirect calls. Hook into implied idt. */
|
|
pmon_monitor_base = address_present ? address : 0xBFC00500;
|
|
idt_monitor_base = pmon_monitor_base - 0x500;
|
|
lsipmon_monitor_base = 0;
|
|
}
|
|
else if (! strncmp (arg, "lsipmon", 7))
|
|
{
|
|
/* lsipmon uses indirect calls. Hook into implied idt. */
|
|
pmon_monitor_base = 0;
|
|
lsipmon_monitor_base = address_present ? address : 0xBFC00200;
|
|
idt_monitor_base = lsipmon_monitor_base - 0x200;
|
|
}
|
|
else if (! strncmp (arg, "none", 4))
|
|
{
|
|
if (address_present)
|
|
{
|
|
sim_io_printf (sd,
|
|
"The `sim firmware none' command does "
|
|
"not take an `ADDRESS' argument.\n");
|
|
return SIM_RC_FAIL;
|
|
}
|
|
idt_monitor_base = 0;
|
|
pmon_monitor_base = 0;
|
|
lsipmon_monitor_base = 0;
|
|
}
|
|
else
|
|
{
|
|
sim_io_printf (sd, "\
|
|
Unrecognized name given to the `sim firmware NAME' command: %s\n\
|
|
Recognized firmware names are: `idt', `pmon', `lsipmon', and `none'.\n",
|
|
arg);
|
|
return SIM_RC_FAIL;
|
|
}
|
|
|
|
return SIM_RC_OK;
|
|
}
|
|
|
|
|
|
|
|
/* Simple monitor interface (currently setup for the IDT and PMON monitors) */
|
|
void
|
|
sim_monitor (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
address_word cia,
|
|
unsigned int reason)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("DBG: sim_monitor: entered (reason = %d)\n",reason);
|
|
#endif /* DEBUG */
|
|
|
|
/* The IDT monitor actually allows two instructions per vector
|
|
slot. However, the simulator currently causes a trap on each
|
|
individual instruction. We cheat, and lose the bottom bit. */
|
|
reason >>= 1;
|
|
|
|
/* The following callback functions are available, however the
|
|
monitor we are simulating does not make use of them: get_errno,
|
|
isatty, lseek, rename, system, time and unlink */
|
|
switch (reason)
|
|
{
|
|
|
|
case 6: /* int open(char *path,int flags) */
|
|
{
|
|
char *path = fetch_str (sd, A0);
|
|
V0 = sim_io_open (sd, path, (int)A1);
|
|
zfree (path);
|
|
break;
|
|
}
|
|
|
|
case 7: /* int read(int file,char *ptr,int len) */
|
|
{
|
|
int fd = A0;
|
|
int nr = A2;
|
|
char *buf = zalloc (nr);
|
|
V0 = sim_io_read (sd, fd, buf, nr);
|
|
sim_write (sd, A1, buf, nr);
|
|
zfree (buf);
|
|
}
|
|
break;
|
|
|
|
case 8: /* int write(int file,char *ptr,int len) */
|
|
{
|
|
int fd = A0;
|
|
int nr = A2;
|
|
char *buf = zalloc (nr);
|
|
sim_read (sd, A1, buf, nr);
|
|
V0 = sim_io_write (sd, fd, buf, nr);
|
|
zfree (buf);
|
|
break;
|
|
}
|
|
|
|
case 10: /* int close(int file) */
|
|
{
|
|
V0 = sim_io_close (sd, (int)A0);
|
|
break;
|
|
}
|
|
|
|
case 2: /* Densan monitor: char inbyte(int waitflag) */
|
|
{
|
|
if (A0 == 0) /* waitflag == NOWAIT */
|
|
V0 = (unsigned_word)-1;
|
|
}
|
|
/* Drop through to case 11 */
|
|
|
|
case 11: /* char inbyte(void) */
|
|
{
|
|
char tmp;
|
|
/* ensure that all output has gone... */
|
|
sim_io_flush_stdout (sd);
|
|
if (sim_io_read_stdin (sd, &tmp, sizeof(char)) != sizeof(char))
|
|
{
|
|
sim_io_error(sd,"Invalid return from character read");
|
|
V0 = (unsigned_word)-1;
|
|
}
|
|
else
|
|
V0 = (unsigned_word)tmp;
|
|
break;
|
|
}
|
|
|
|
case 3: /* Densan monitor: void co(char chr) */
|
|
case 12: /* void outbyte(char chr) : write a byte to "stdout" */
|
|
{
|
|
char tmp = (char)(A0 & 0xFF);
|
|
sim_io_write_stdout (sd, &tmp, sizeof(char));
|
|
break;
|
|
}
|
|
|
|
case 17: /* void _exit() */
|
|
{
|
|
sim_io_eprintf (sd, "sim_monitor(17): _exit(int reason) to be coded\n");
|
|
sim_engine_halt (SD, CPU, NULL, NULL_CIA, sim_exited,
|
|
(unsigned int)(A0 & 0xFFFFFFFF));
|
|
break;
|
|
}
|
|
|
|
case 28 : /* PMON flush_cache */
|
|
break;
|
|
|
|
case 55: /* void get_mem_info(unsigned int *ptr) */
|
|
/* in: A0 = pointer to three word memory location */
|
|
/* out: [A0 + 0] = size */
|
|
/* [A0 + 4] = instruction cache size */
|
|
/* [A0 + 8] = data cache size */
|
|
{
|
|
unsigned_4 value = MEM_SIZE /* FIXME STATE_MEM_SIZE (sd) */;
|
|
unsigned_4 zero = 0;
|
|
H2T (value);
|
|
sim_write (sd, A0 + 0, (char *)&value, 4);
|
|
sim_write (sd, A0 + 4, (char *)&zero, 4);
|
|
sim_write (sd, A0 + 8, (char *)&zero, 4);
|
|
/* sim_io_eprintf (sd, "sim: get_mem_info() depreciated\n"); */
|
|
break;
|
|
}
|
|
|
|
case 158 : /* PMON printf */
|
|
/* in: A0 = pointer to format string */
|
|
/* A1 = optional argument 1 */
|
|
/* A2 = optional argument 2 */
|
|
/* A3 = optional argument 3 */
|
|
/* out: void */
|
|
/* The following is based on the PMON printf source */
|
|
{
|
|
address_word s = A0;
|
|
char c;
|
|
signed_word *ap = &A1; /* 1st argument */
|
|
/* This isn't the quickest way, since we call the host print
|
|
routine for every character almost. But it does avoid
|
|
having to allocate and manage a temporary string buffer. */
|
|
/* TODO: Include check that we only use three arguments (A1,
|
|
A2 and A3) */
|
|
while (sim_read (sd, s++, &c, 1) && c != '\0')
|
|
{
|
|
if (c == '%')
|
|
{
|
|
char tmp[40];
|
|
enum {FMT_RJUST, FMT_LJUST, FMT_RJUST0, FMT_CENTER} fmt = FMT_RJUST;
|
|
int width = 0, trunc = 0, haddot = 0, longlong = 0;
|
|
while (sim_read (sd, s++, &c, 1) && c != '\0')
|
|
{
|
|
if (strchr ("dobxXulscefg%", c))
|
|
break;
|
|
else if (c == '-')
|
|
fmt = FMT_LJUST;
|
|
else if (c == '0')
|
|
fmt = FMT_RJUST0;
|
|
else if (c == '~')
|
|
fmt = FMT_CENTER;
|
|
else if (c == '*')
|
|
{
|
|
if (haddot)
|
|
trunc = (int)*ap++;
|
|
else
|
|
width = (int)*ap++;
|
|
}
|
|
else if (c >= '1' && c <= '9')
|
|
{
|
|
address_word t = s;
|
|
unsigned int n;
|
|
while (sim_read (sd, s++, &c, 1) == 1 && isdigit (c))
|
|
tmp[s - t] = c;
|
|
tmp[s - t] = '\0';
|
|
n = (unsigned int)strtol(tmp,NULL,10);
|
|
if (haddot)
|
|
trunc = n;
|
|
else
|
|
width = n;
|
|
s--;
|
|
}
|
|
else if (c == '.')
|
|
haddot = 1;
|
|
}
|
|
switch (c)
|
|
{
|
|
case '%':
|
|
sim_io_printf (sd, "%%");
|
|
break;
|
|
case 's':
|
|
if ((int)*ap != 0)
|
|
{
|
|
address_word p = *ap++;
|
|
char ch;
|
|
while (sim_read (sd, p++, &ch, 1) == 1 && ch != '\0')
|
|
sim_io_printf(sd, "%c", ch);
|
|
}
|
|
else
|
|
sim_io_printf(sd,"(null)");
|
|
break;
|
|
case 'c':
|
|
sim_io_printf (sd, "%c", (int)*ap++);
|
|
break;
|
|
default:
|
|
if (c == 'l')
|
|
{
|
|
sim_read (sd, s++, &c, 1);
|
|
if (c == 'l')
|
|
{
|
|
longlong = 1;
|
|
sim_read (sd, s++, &c, 1);
|
|
}
|
|
}
|
|
if (strchr ("dobxXu", c))
|
|
{
|
|
word64 lv = (word64) *ap++;
|
|
if (c == 'b')
|
|
sim_io_printf(sd,"<binary not supported>");
|
|
else
|
|
{
|
|
sprintf (tmp, "%%%s%c", longlong ? "ll" : "", c);
|
|
if (longlong)
|
|
sim_io_printf(sd, tmp, lv);
|
|
else
|
|
sim_io_printf(sd, tmp, (int)lv);
|
|
}
|
|
}
|
|
else if (strchr ("eEfgG", c))
|
|
{
|
|
double dbl = *(double*)(ap++);
|
|
sprintf (tmp, "%%%d.%d%c", width, trunc, c);
|
|
sim_io_printf (sd, tmp, dbl);
|
|
trunc = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
sim_io_printf(sd, "%c", c);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
sim_io_error (sd, "TODO: sim_monitor(%d) : PC = 0x%s\n",
|
|
reason, pr_addr(cia));
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Store a word into memory. */
|
|
|
|
static void
|
|
store_word (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
address_word cia,
|
|
uword64 vaddr,
|
|
signed_word val)
|
|
{
|
|
address_word paddr;
|
|
int uncached;
|
|
|
|
if ((vaddr & 3) != 0)
|
|
SignalExceptionAddressStore ();
|
|
else
|
|
{
|
|
if (AddressTranslation (vaddr, isDATA, isSTORE, &paddr, &uncached,
|
|
isTARGET, isREAL))
|
|
{
|
|
const uword64 mask = 7;
|
|
uword64 memval;
|
|
unsigned int byte;
|
|
|
|
paddr = (paddr & ~mask) | ((paddr & mask) ^ (ReverseEndian << 2));
|
|
byte = (vaddr & mask) ^ (BigEndianCPU << 2);
|
|
memval = ((uword64) val) << (8 * byte);
|
|
StoreMemory (uncached, AccessLength_WORD, memval, 0, paddr, vaddr,
|
|
isREAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Load a word from memory. */
|
|
|
|
static signed_word
|
|
load_word (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
address_word cia,
|
|
uword64 vaddr)
|
|
{
|
|
if ((vaddr & 3) != 0)
|
|
{
|
|
SIM_CORE_SIGNAL (SD, cpu, cia, read_map, AccessLength_WORD+1, vaddr, read_transfer, sim_core_unaligned_signal);
|
|
}
|
|
else
|
|
{
|
|
address_word paddr;
|
|
int uncached;
|
|
|
|
if (AddressTranslation (vaddr, isDATA, isLOAD, &paddr, &uncached,
|
|
isTARGET, isREAL))
|
|
{
|
|
const uword64 mask = 0x7;
|
|
const unsigned int reverse = ReverseEndian ? 1 : 0;
|
|
const unsigned int bigend = BigEndianCPU ? 1 : 0;
|
|
uword64 memval;
|
|
unsigned int byte;
|
|
|
|
paddr = (paddr & ~mask) | ((paddr & mask) ^ (reverse << 2));
|
|
LoadMemory (&memval,NULL,uncached, AccessLength_WORD, paddr, vaddr,
|
|
isDATA, isREAL);
|
|
byte = (vaddr & mask) ^ (bigend << 2);
|
|
return SIGNEXTEND (((memval >> (8 * byte)) & 0xffffffff), 32);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Simulate the mips16 entry and exit pseudo-instructions. These
|
|
would normally be handled by the reserved instruction exception
|
|
code, but for ease of simulation we just handle them directly. */
|
|
|
|
static void
|
|
mips16_entry (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
address_word cia,
|
|
unsigned int insn)
|
|
{
|
|
int aregs, sregs, rreg;
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: mips16_entry: entered (insn = 0x%08X)\n",insn);
|
|
#endif /* DEBUG */
|
|
|
|
aregs = (insn & 0x700) >> 8;
|
|
sregs = (insn & 0x0c0) >> 6;
|
|
rreg = (insn & 0x020) >> 5;
|
|
|
|
/* This should be checked by the caller. */
|
|
if (sregs == 3)
|
|
abort ();
|
|
|
|
if (aregs < 5)
|
|
{
|
|
int i;
|
|
signed_word tsp;
|
|
|
|
/* This is the entry pseudo-instruction. */
|
|
|
|
for (i = 0; i < aregs; i++)
|
|
store_word (SD, CPU, cia, (uword64) (SP + 4 * i), GPR[i + 4]);
|
|
|
|
tsp = SP;
|
|
SP -= 32;
|
|
|
|
if (rreg)
|
|
{
|
|
tsp -= 4;
|
|
store_word (SD, CPU, cia, (uword64) tsp, RA);
|
|
}
|
|
|
|
for (i = 0; i < sregs; i++)
|
|
{
|
|
tsp -= 4;
|
|
store_word (SD, CPU, cia, (uword64) tsp, GPR[16 + i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
signed_word tsp;
|
|
|
|
/* This is the exit pseudo-instruction. */
|
|
|
|
tsp = SP + 32;
|
|
|
|
if (rreg)
|
|
{
|
|
tsp -= 4;
|
|
RA = load_word (SD, CPU, cia, (uword64) tsp);
|
|
}
|
|
|
|
for (i = 0; i < sregs; i++)
|
|
{
|
|
tsp -= 4;
|
|
GPR[i + 16] = load_word (SD, CPU, cia, (uword64) tsp);
|
|
}
|
|
|
|
SP += 32;
|
|
|
|
if (CURRENT_FLOATING_POINT == HARD_FLOATING_POINT)
|
|
{
|
|
if (aregs == 5)
|
|
{
|
|
FGR[0] = WORD64LO (GPR[4]);
|
|
FPR_STATE[0] = fmt_uninterpreted;
|
|
}
|
|
else if (aregs == 6)
|
|
{
|
|
FGR[0] = WORD64LO (GPR[5]);
|
|
FGR[1] = WORD64LO (GPR[4]);
|
|
FPR_STATE[0] = fmt_uninterpreted;
|
|
FPR_STATE[1] = fmt_uninterpreted;
|
|
}
|
|
}
|
|
|
|
PC = RA;
|
|
}
|
|
|
|
}
|
|
|
|
/*-- trace support ----------------------------------------------------------*/
|
|
|
|
/* The TRACE support is provided (if required) in the memory accessing
|
|
routines. Since we are also providing the architecture specific
|
|
features, the architecture simulation code can also deal with
|
|
notifying the TRACE world of cache flushes, etc. Similarly we do
|
|
not need to provide profiling support in the simulator engine,
|
|
since we can sample in the instruction fetch control loop. By
|
|
defining the TRACE manifest, we add tracing as a run-time
|
|
option. */
|
|
|
|
#if defined(TRACE)
|
|
/* Tracing by default produces "din" format (as required by
|
|
dineroIII). Each line of such a trace file *MUST* have a din label
|
|
and address field. The rest of the line is ignored, so comments can
|
|
be included if desired. The first field is the label which must be
|
|
one of the following values:
|
|
|
|
0 read data
|
|
1 write data
|
|
2 instruction fetch
|
|
3 escape record (treated as unknown access type)
|
|
4 escape record (causes cache flush)
|
|
|
|
The address field is a 32bit (lower-case) hexadecimal address
|
|
value. The address should *NOT* be preceded by "0x".
|
|
|
|
The size of the memory transfer is not important when dealing with
|
|
cache lines (as long as no more than a cache line can be
|
|
transferred in a single operation :-), however more information
|
|
could be given following the dineroIII requirement to allow more
|
|
complete memory and cache simulators to provide better
|
|
results. i.e. the University of Pisa has a cache simulator that can
|
|
also take bus size and speed as (variable) inputs to calculate
|
|
complete system performance (a much more useful ability when trying
|
|
to construct an end product, rather than a processor). They
|
|
currently have an ARM version of their tool called ChARM. */
|
|
|
|
|
|
void
|
|
dotrace (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
FILE *tracefh,
|
|
int type,
|
|
SIM_ADDR address,
|
|
int width,
|
|
char *comment,...)
|
|
{
|
|
if (STATE & simTRACE) {
|
|
va_list ap;
|
|
fprintf(tracefh,"%d %s ; width %d ; ",
|
|
type,
|
|
pr_addr(address),
|
|
width);
|
|
va_start(ap,comment);
|
|
vfprintf(tracefh,comment,ap);
|
|
va_end(ap);
|
|
fprintf(tracefh,"\n");
|
|
}
|
|
/* NOTE: Since the "din" format will only accept 32bit addresses, and
|
|
we may be generating 64bit ones, we should put the hi-32bits of the
|
|
address into the comment field. */
|
|
|
|
/* TODO: Provide a buffer for the trace lines. We can then avoid
|
|
performing writes until the buffer is filled, or the file is
|
|
being closed. */
|
|
|
|
/* NOTE: We could consider adding a comment field to the "din" file
|
|
produced using type 3 markers (unknown access). This would then
|
|
allow information about the program that the "din" is for, and
|
|
the MIPs world that was being simulated, to be placed into the
|
|
trace file. */
|
|
|
|
return;
|
|
}
|
|
#endif /* TRACE */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/*-- simulator engine -------------------------------------------------------*/
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void
|
|
ColdReset (SIM_DESC sd)
|
|
{
|
|
int cpu_nr;
|
|
for (cpu_nr = 0; cpu_nr < sim_engine_nr_cpus (sd); cpu_nr++)
|
|
{
|
|
sim_cpu *cpu = STATE_CPU (sd, cpu_nr);
|
|
/* RESET: Fixed PC address: */
|
|
PC = (unsigned_word) UNSIGNED64 (0xFFFFFFFFBFC00000);
|
|
/* The reset vector address is in the unmapped, uncached memory space. */
|
|
|
|
SR &= ~(status_SR | status_TS | status_RP);
|
|
SR |= (status_ERL | status_BEV);
|
|
|
|
/* Cheat and allow access to the complete register set immediately */
|
|
if (CURRENT_FLOATING_POINT == HARD_FLOATING_POINT
|
|
&& WITH_TARGET_WORD_BITSIZE == 64)
|
|
SR |= status_FR; /* 64bit registers */
|
|
|
|
/* Ensure that any instructions with pending register updates are
|
|
cleared: */
|
|
PENDING_INVALIDATE();
|
|
|
|
/* Initialise the FPU registers to the unknown state */
|
|
if (CURRENT_FLOATING_POINT == HARD_FLOATING_POINT)
|
|
{
|
|
int rn;
|
|
for (rn = 0; (rn < 32); rn++)
|
|
FPR_STATE[rn] = fmt_uninterpreted;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Description from page A-26 of the "MIPS IV Instruction Set" manual (revision 3.1) */
|
|
/* Signal an exception condition. This will result in an exception
|
|
that aborts the instruction. The instruction operation pseudocode
|
|
will never see a return from this function call. */
|
|
|
|
void
|
|
signal_exception (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
address_word cia,
|
|
int exception,...)
|
|
{
|
|
/* int vector; */
|
|
|
|
#ifdef DEBUG
|
|
sim_io_printf(sd,"DBG: SignalException(%d) PC = 0x%s\n",exception,pr_addr(cia));
|
|
#endif /* DEBUG */
|
|
|
|
/* Ensure that any active atomic read/modify/write operation will fail: */
|
|
LLBIT = 0;
|
|
|
|
/* Save registers before interrupt dispatching */
|
|
#ifdef SIM_CPU_EXCEPTION_TRIGGER
|
|
SIM_CPU_EXCEPTION_TRIGGER(sd, cpu, cia);
|
|
#endif
|
|
|
|
switch (exception) {
|
|
|
|
case DebugBreakPoint :
|
|
if (! (Debug & Debug_DM))
|
|
{
|
|
if (INDELAYSLOT())
|
|
{
|
|
CANCELDELAYSLOT();
|
|
|
|
Debug |= Debug_DBD; /* signaled from within in delay slot */
|
|
DEPC = cia - 4; /* reference the branch instruction */
|
|
}
|
|
else
|
|
{
|
|
Debug &= ~Debug_DBD; /* not signaled from within a delay slot */
|
|
DEPC = cia;
|
|
}
|
|
|
|
Debug |= Debug_DM; /* in debugging mode */
|
|
Debug |= Debug_DBp; /* raising a DBp exception */
|
|
PC = 0xBFC00200;
|
|
sim_engine_restart (SD, CPU, NULL, NULL_CIA);
|
|
}
|
|
break;
|
|
|
|
case ReservedInstruction :
|
|
{
|
|
va_list ap;
|
|
unsigned int instruction;
|
|
va_start(ap,exception);
|
|
instruction = va_arg(ap,unsigned int);
|
|
va_end(ap);
|
|
/* Provide simple monitor support using ReservedInstruction
|
|
exceptions. The following code simulates the fixed vector
|
|
entry points into the IDT monitor by causing a simulator
|
|
trap, performing the monitor operation, and returning to
|
|
the address held in the $ra register (standard PCS return
|
|
address). This means we only need to pre-load the vector
|
|
space with suitable instruction values. For systems were
|
|
actual trap instructions are used, we would not need to
|
|
perform this magic. */
|
|
if ((instruction & RSVD_INSTRUCTION_MASK) == RSVD_INSTRUCTION)
|
|
{
|
|
sim_monitor (SD, CPU, cia, ((instruction >> RSVD_INSTRUCTION_ARG_SHIFT) & RSVD_INSTRUCTION_ARG_MASK) );
|
|
/* NOTE: This assumes that a branch-and-link style
|
|
instruction was used to enter the vector (which is the
|
|
case with the current IDT monitor). */
|
|
sim_engine_restart (SD, CPU, NULL, RA);
|
|
}
|
|
/* Look for the mips16 entry and exit instructions, and
|
|
simulate a handler for them. */
|
|
else if ((cia & 1) != 0
|
|
&& (instruction & 0xf81f) == 0xe809
|
|
&& (instruction & 0x0c0) != 0x0c0)
|
|
{
|
|
mips16_entry (SD, CPU, cia, instruction);
|
|
sim_engine_restart (sd, NULL, NULL, NULL_CIA);
|
|
}
|
|
/* else fall through to normal exception processing */
|
|
sim_io_eprintf(sd,"ReservedInstruction at PC = 0x%s\n", pr_addr (cia));
|
|
}
|
|
|
|
default:
|
|
/* Store exception code into current exception id variable (used
|
|
by exit code): */
|
|
|
|
/* TODO: If not simulating exceptions then stop the simulator
|
|
execution. At the moment we always stop the simulation. */
|
|
|
|
#ifdef SUBTARGET_R3900
|
|
/* update interrupt-related registers */
|
|
|
|
/* insert exception code in bits 6:2 */
|
|
CAUSE = LSMASKED32(CAUSE, 31, 7) | LSINSERTED32(exception, 6, 2);
|
|
/* shift IE/KU history bits left */
|
|
SR = LSMASKED32(SR, 31, 4) | LSINSERTED32(LSEXTRACTED32(SR, 3, 0), 5, 2);
|
|
|
|
if (STATE & simDELAYSLOT)
|
|
{
|
|
STATE &= ~simDELAYSLOT;
|
|
CAUSE |= cause_BD;
|
|
EPC = (cia - 4); /* reference the branch instruction */
|
|
}
|
|
else
|
|
EPC = cia;
|
|
|
|
if (SR & status_BEV)
|
|
PC = (signed)0xBFC00000 + 0x180;
|
|
else
|
|
PC = (signed)0x80000000 + 0x080;
|
|
#else
|
|
/* See figure 5-17 for an outline of the code below */
|
|
if (! (SR & status_EXL))
|
|
{
|
|
CAUSE = (exception << 2);
|
|
if (STATE & simDELAYSLOT)
|
|
{
|
|
STATE &= ~simDELAYSLOT;
|
|
CAUSE |= cause_BD;
|
|
EPC = (cia - 4); /* reference the branch instruction */
|
|
}
|
|
else
|
|
EPC = cia;
|
|
/* FIXME: TLB et.al. */
|
|
/* vector = 0x180; */
|
|
}
|
|
else
|
|
{
|
|
CAUSE = (exception << 2);
|
|
/* vector = 0x180; */
|
|
}
|
|
SR |= status_EXL;
|
|
/* Store exception code into current exception id variable (used
|
|
by exit code): */
|
|
|
|
if (SR & status_BEV)
|
|
PC = (signed)0xBFC00200 + 0x180;
|
|
else
|
|
PC = (signed)0x80000000 + 0x180;
|
|
#endif
|
|
|
|
switch ((CAUSE >> 2) & 0x1F)
|
|
{
|
|
case Interrupt:
|
|
/* Interrupts arrive during event processing, no need to
|
|
restart */
|
|
return;
|
|
|
|
case NMIReset:
|
|
/* Ditto */
|
|
#ifdef SUBTARGET_3900
|
|
/* Exception vector: BEV=0 BFC00000 / BEF=1 BFC00000 */
|
|
PC = (signed)0xBFC00000;
|
|
#endif SUBTARGET_3900
|
|
return;
|
|
|
|
case TLBModification:
|
|
case TLBLoad:
|
|
case TLBStore:
|
|
case AddressLoad:
|
|
case AddressStore:
|
|
case InstructionFetch:
|
|
case DataReference:
|
|
/* The following is so that the simulator will continue from the
|
|
exception handler address. */
|
|
sim_engine_halt (SD, CPU, NULL, PC,
|
|
sim_stopped, SIM_SIGBUS);
|
|
|
|
case ReservedInstruction:
|
|
case CoProcessorUnusable:
|
|
PC = EPC;
|
|
sim_engine_halt (SD, CPU, NULL, PC,
|
|
sim_stopped, SIM_SIGILL);
|
|
|
|
case IntegerOverflow:
|
|
case FPE:
|
|
sim_engine_halt (SD, CPU, NULL, PC,
|
|
sim_stopped, SIM_SIGFPE);
|
|
|
|
case BreakPoint:
|
|
sim_engine_halt (SD, CPU, NULL, PC, sim_stopped, SIM_SIGTRAP);
|
|
break;
|
|
|
|
case SystemCall:
|
|
case Trap:
|
|
sim_engine_restart (SD, CPU, NULL, PC);
|
|
break;
|
|
|
|
case Watch:
|
|
PC = EPC;
|
|
sim_engine_halt (SD, CPU, NULL, PC,
|
|
sim_stopped, SIM_SIGTRAP);
|
|
|
|
default : /* Unknown internal exception */
|
|
PC = EPC;
|
|
sim_engine_halt (SD, CPU, NULL, PC,
|
|
sim_stopped, SIM_SIGABRT);
|
|
|
|
}
|
|
|
|
case SimulatorFault:
|
|
{
|
|
va_list ap;
|
|
char *msg;
|
|
va_start(ap,exception);
|
|
msg = va_arg(ap,char *);
|
|
va_end(ap);
|
|
sim_engine_abort (SD, CPU, NULL_CIA,
|
|
"FATAL: Simulator error \"%s\"\n",msg);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
#if defined(WARN_RESULT)
|
|
/* Description from page A-26 of the "MIPS IV Instruction Set" manual (revision 3.1) */
|
|
/* This function indicates that the result of the operation is
|
|
undefined. However, this should not affect the instruction
|
|
stream. All that is meant to happen is that the destination
|
|
register is set to an undefined result. To keep the simulator
|
|
simple, we just don't bother updating the destination register, so
|
|
the overall result will be undefined. If desired we can stop the
|
|
simulator by raising a pseudo-exception. */
|
|
#define UndefinedResult() undefined_result (sd,cia)
|
|
static void
|
|
undefined_result(sd,cia)
|
|
SIM_DESC sd;
|
|
address_word cia;
|
|
{
|
|
sim_io_eprintf(sd,"UndefinedResult: PC = 0x%s\n",pr_addr(cia));
|
|
#if 0 /* Disabled for the moment, since it actually happens a lot at the moment. */
|
|
state |= simSTOP;
|
|
#endif
|
|
return;
|
|
}
|
|
#endif /* WARN_RESULT */
|
|
|
|
/*-- FPU support routines ---------------------------------------------------*/
|
|
|
|
/* Numbers are held in normalized form. The SINGLE and DOUBLE binary
|
|
formats conform to ANSI/IEEE Std 754-1985. */
|
|
/* SINGLE precision floating:
|
|
* seeeeeeeefffffffffffffffffffffff
|
|
* s = 1bit = sign
|
|
* e = 8bits = exponent
|
|
* f = 23bits = fraction
|
|
*/
|
|
/* SINGLE precision fixed:
|
|
* siiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
|
|
* s = 1bit = sign
|
|
* i = 31bits = integer
|
|
*/
|
|
/* DOUBLE precision floating:
|
|
* seeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|
* s = 1bit = sign
|
|
* e = 11bits = exponent
|
|
* f = 52bits = fraction
|
|
*/
|
|
/* DOUBLE precision fixed:
|
|
* siiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
|
|
* s = 1bit = sign
|
|
* i = 63bits = integer
|
|
*/
|
|
|
|
/* Extract sign-bit: */
|
|
#define FP_S_s(v) (((v) & ((unsigned)1 << 31)) ? 1 : 0)
|
|
#define FP_D_s(v) (((v) & ((uword64)1 << 63)) ? 1 : 0)
|
|
/* Extract biased exponent: */
|
|
#define FP_S_be(v) (((v) >> 23) & 0xFF)
|
|
#define FP_D_be(v) (((v) >> 52) & 0x7FF)
|
|
/* Extract unbiased Exponent: */
|
|
#define FP_S_e(v) (FP_S_be(v) - 0x7F)
|
|
#define FP_D_e(v) (FP_D_be(v) - 0x3FF)
|
|
/* Extract complete fraction field: */
|
|
#define FP_S_f(v) ((v) & ~((unsigned)0x1FF << 23))
|
|
#define FP_D_f(v) ((v) & ~((uword64)0xFFF << 52))
|
|
/* Extract numbered fraction bit: */
|
|
#define FP_S_fb(b,v) (((v) & (1 << (23 - (b)))) ? 1 : 0)
|
|
#define FP_D_fb(b,v) (((v) & (1 << (52 - (b)))) ? 1 : 0)
|
|
|
|
/* Explicit QNaN values used when value required: */
|
|
#define FPQNaN_SINGLE (0x7FBFFFFF)
|
|
#define FPQNaN_WORD (0x7FFFFFFF)
|
|
#define FPQNaN_DOUBLE (((uword64)0x7FF7FFFF << 32) | 0xFFFFFFFF)
|
|
#define FPQNaN_LONG (((uword64)0x7FFFFFFF << 32) | 0xFFFFFFFF)
|
|
|
|
/* Explicit Infinity values used when required: */
|
|
#define FPINF_SINGLE (0x7F800000)
|
|
#define FPINF_DOUBLE (((uword64)0x7FF00000 << 32) | 0x00000000)
|
|
|
|
#define RMMODE(v) (((v) == FP_RM_NEAREST) ? "Round" : (((v) == FP_RM_TOZERO) ? "Trunc" : (((v) == FP_RM_TOPINF) ? "Ceil" : "Floor")))
|
|
#define DOFMT(v) (((v) == fmt_single) ? "single" : (((v) == fmt_double) ? "double" : (((v) == fmt_word) ? "word" : (((v) == fmt_long) ? "long" : (((v) == fmt_unknown) ? "<unknown>" : (((v) == fmt_uninterpreted) ? "<uninterpreted>" : (((v) == fmt_uninterpreted_32) ? "<uninterpreted_32>" : (((v) == fmt_uninterpreted_64) ? "<uninterpreted_64>" : "<format error>"))))))))
|
|
|
|
uword64
|
|
value_fpr (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
address_word cia,
|
|
int fpr,
|
|
FP_formats fmt)
|
|
{
|
|
uword64 value = 0;
|
|
int err = 0;
|
|
|
|
/* Treat unused register values, as fixed-point 64bit values: */
|
|
if ((fmt == fmt_uninterpreted) || (fmt == fmt_unknown))
|
|
#if 1
|
|
/* If request to read data as "uninterpreted", then use the current
|
|
encoding: */
|
|
fmt = FPR_STATE[fpr];
|
|
#else
|
|
fmt = fmt_long;
|
|
#endif
|
|
|
|
/* For values not yet accessed, set to the desired format: */
|
|
if (FPR_STATE[fpr] == fmt_uninterpreted) {
|
|
FPR_STATE[fpr] = fmt;
|
|
#ifdef DEBUG
|
|
printf("DBG: Register %d was fmt_uninterpreted. Now %s\n",fpr,DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
}
|
|
if (fmt != FPR_STATE[fpr]) {
|
|
sim_io_eprintf(sd,"FPR %d (format %s) being accessed with format %s - setting to unknown (PC = 0x%s)\n",fpr,DOFMT(FPR_STATE[fpr]),DOFMT(fmt),pr_addr(cia));
|
|
FPR_STATE[fpr] = fmt_unknown;
|
|
}
|
|
|
|
if (FPR_STATE[fpr] == fmt_unknown) {
|
|
/* Set QNaN value: */
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
value = FPQNaN_SINGLE;
|
|
break;
|
|
|
|
case fmt_double:
|
|
value = FPQNaN_DOUBLE;
|
|
break;
|
|
|
|
case fmt_word:
|
|
value = FPQNaN_WORD;
|
|
break;
|
|
|
|
case fmt_long:
|
|
value = FPQNaN_LONG;
|
|
break;
|
|
|
|
default:
|
|
err = -1;
|
|
break;
|
|
}
|
|
} else if (SizeFGR() == 64) {
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
case fmt_word:
|
|
value = (FGR[fpr] & 0xFFFFFFFF);
|
|
break;
|
|
|
|
case fmt_uninterpreted:
|
|
case fmt_double:
|
|
case fmt_long:
|
|
value = FGR[fpr];
|
|
break;
|
|
|
|
default :
|
|
err = -1;
|
|
break;
|
|
}
|
|
} else {
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
case fmt_word:
|
|
value = (FGR[fpr] & 0xFFFFFFFF);
|
|
break;
|
|
|
|
case fmt_uninterpreted:
|
|
case fmt_double:
|
|
case fmt_long:
|
|
if ((fpr & 1) == 0) { /* even registers only */
|
|
#ifdef DEBUG
|
|
printf("DBG: ValueFPR: FGR[%d] = %s, FGR[%d] = %s\n",
|
|
fpr+1, pr_uword64( (uword64) FGR[fpr+1] ),
|
|
fpr, pr_uword64( (uword64) FGR[fpr] ));
|
|
#endif
|
|
value = ((((uword64)FGR[fpr+1]) << 32) | (FGR[fpr] & 0xFFFFFFFF));
|
|
} else {
|
|
SignalException(ReservedInstruction,0);
|
|
}
|
|
break;
|
|
|
|
default :
|
|
err = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (err)
|
|
SignalExceptionSimulatorFault ("Unrecognised FP format in ValueFPR()");
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: ValueFPR: fpr = %d, fmt = %s, value = 0x%s : PC = 0x%s : SizeFGR() = %d\n",fpr,DOFMT(fmt),pr_uword64(value),pr_addr(cia),SizeFGR());
|
|
#endif /* DEBUG */
|
|
|
|
return(value);
|
|
}
|
|
|
|
void
|
|
store_fpr (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
address_word cia,
|
|
int fpr,
|
|
FP_formats fmt,
|
|
uword64 value)
|
|
{
|
|
int err = 0;
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: StoreFPR: fpr = %d, fmt = %s, value = 0x%s : PC = 0x%s : SizeFGR() = %d,\n",fpr,DOFMT(fmt),pr_uword64(value),pr_addr(cia),SizeFGR());
|
|
#endif /* DEBUG */
|
|
|
|
if (SizeFGR() == 64) {
|
|
switch (fmt) {
|
|
case fmt_uninterpreted_32:
|
|
fmt = fmt_uninterpreted;
|
|
case fmt_single :
|
|
case fmt_word :
|
|
FGR[fpr] = (((uword64)0xDEADC0DE << 32) | (value & 0xFFFFFFFF));
|
|
FPR_STATE[fpr] = fmt;
|
|
break;
|
|
|
|
case fmt_uninterpreted_64:
|
|
fmt = fmt_uninterpreted;
|
|
case fmt_uninterpreted:
|
|
case fmt_double :
|
|
case fmt_long :
|
|
FGR[fpr] = value;
|
|
FPR_STATE[fpr] = fmt;
|
|
break;
|
|
|
|
default :
|
|
FPR_STATE[fpr] = fmt_unknown;
|
|
err = -1;
|
|
break;
|
|
}
|
|
} else {
|
|
switch (fmt) {
|
|
case fmt_uninterpreted_32:
|
|
fmt = fmt_uninterpreted;
|
|
case fmt_single :
|
|
case fmt_word :
|
|
FGR[fpr] = (value & 0xFFFFFFFF);
|
|
FPR_STATE[fpr] = fmt;
|
|
break;
|
|
|
|
case fmt_uninterpreted_64:
|
|
fmt = fmt_uninterpreted;
|
|
case fmt_uninterpreted:
|
|
case fmt_double :
|
|
case fmt_long :
|
|
if ((fpr & 1) == 0) { /* even register number only */
|
|
FGR[fpr+1] = (value >> 32);
|
|
FGR[fpr] = (value & 0xFFFFFFFF);
|
|
FPR_STATE[fpr + 1] = fmt;
|
|
FPR_STATE[fpr] = fmt;
|
|
} else {
|
|
FPR_STATE[fpr] = fmt_unknown;
|
|
FPR_STATE[fpr + 1] = fmt_unknown;
|
|
SignalException(ReservedInstruction,0);
|
|
}
|
|
break;
|
|
|
|
default :
|
|
FPR_STATE[fpr] = fmt_unknown;
|
|
err = -1;
|
|
break;
|
|
}
|
|
}
|
|
#if defined(WARN_RESULT)
|
|
else
|
|
UndefinedResult();
|
|
#endif /* WARN_RESULT */
|
|
|
|
if (err)
|
|
SignalExceptionSimulatorFault ("Unrecognised FP format in StoreFPR()");
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: StoreFPR: fpr[%d] = 0x%s (format %s)\n",fpr,pr_uword64(FGR[fpr]),DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
|
|
return;
|
|
}
|
|
|
|
int
|
|
NaN(op,fmt)
|
|
uword64 op;
|
|
FP_formats fmt;
|
|
{
|
|
int boolean = 0;
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
case fmt_word:
|
|
{
|
|
sim_fpu wop;
|
|
sim_fpu_32to (&wop, op);
|
|
boolean = sim_fpu_is_nan (&wop);
|
|
break;
|
|
}
|
|
case fmt_double:
|
|
case fmt_long:
|
|
{
|
|
sim_fpu wop;
|
|
sim_fpu_64to (&wop, op);
|
|
boolean = sim_fpu_is_nan (&wop);
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: NaN: returning %d for 0x%s (format = %s)\n",boolean,pr_addr(op),DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
|
|
return(boolean);
|
|
}
|
|
|
|
int
|
|
Infinity(op,fmt)
|
|
uword64 op;
|
|
FP_formats fmt;
|
|
{
|
|
int boolean = 0;
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Infinity: format %s 0x%s\n",DOFMT(fmt),pr_addr(op));
|
|
#endif /* DEBUG */
|
|
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
{
|
|
sim_fpu wop;
|
|
sim_fpu_32to (&wop, op);
|
|
boolean = sim_fpu_is_infinity (&wop);
|
|
break;
|
|
}
|
|
case fmt_double:
|
|
{
|
|
sim_fpu wop;
|
|
sim_fpu_64to (&wop, op);
|
|
boolean = sim_fpu_is_infinity (&wop);
|
|
break;
|
|
}
|
|
default:
|
|
printf("DBG: TODO: unrecognised format (%s) for Infinity check\n",DOFMT(fmt));
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Infinity: returning %d for 0x%s (format = %s)\n",boolean,pr_addr(op),DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
|
|
return(boolean);
|
|
}
|
|
|
|
int
|
|
Less(op1,op2,fmt)
|
|
uword64 op1;
|
|
uword64 op2;
|
|
FP_formats fmt;
|
|
{
|
|
int boolean = 0;
|
|
|
|
/* Argument checking already performed by the FPCOMPARE code */
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Less: %s: op1 = 0x%s : op2 = 0x%s\n",DOFMT(fmt),pr_addr(op1),pr_addr(op2));
|
|
#endif /* DEBUG */
|
|
|
|
/* The format type should already have been checked: */
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu_32to (&wop1, op1);
|
|
sim_fpu_32to (&wop2, op2);
|
|
boolean = sim_fpu_is_lt (&wop1, &wop2);
|
|
break;
|
|
}
|
|
case fmt_double:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu_64to (&wop1, op1);
|
|
sim_fpu_64to (&wop2, op2);
|
|
boolean = sim_fpu_is_lt (&wop1, &wop2);
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Less: returning %d (format = %s)\n",boolean,DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
|
|
return(boolean);
|
|
}
|
|
|
|
int
|
|
Equal(op1,op2,fmt)
|
|
uword64 op1;
|
|
uword64 op2;
|
|
FP_formats fmt;
|
|
{
|
|
int boolean = 0;
|
|
|
|
/* Argument checking already performed by the FPCOMPARE code */
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Equal: %s: op1 = 0x%s : op2 = 0x%s\n",DOFMT(fmt),pr_addr(op1),pr_addr(op2));
|
|
#endif /* DEBUG */
|
|
|
|
/* The format type should already have been checked: */
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu_32to (&wop1, op1);
|
|
sim_fpu_32to (&wop2, op2);
|
|
boolean = sim_fpu_is_eq (&wop1, &wop2);
|
|
break;
|
|
}
|
|
case fmt_double:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu_64to (&wop1, op1);
|
|
sim_fpu_64to (&wop2, op2);
|
|
boolean = sim_fpu_is_eq (&wop1, &wop2);
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Equal: returning %d (format = %s)\n",boolean,DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
|
|
return(boolean);
|
|
}
|
|
|
|
uword64
|
|
AbsoluteValue(op,fmt)
|
|
uword64 op;
|
|
FP_formats fmt;
|
|
{
|
|
uword64 result = 0;
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: AbsoluteValue: %s: op = 0x%s\n",DOFMT(fmt),pr_addr(op));
|
|
#endif /* DEBUG */
|
|
|
|
/* The format type should already have been checked: */
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
{
|
|
sim_fpu wop;
|
|
unsigned32 ans;
|
|
sim_fpu_32to (&wop, op);
|
|
sim_fpu_abs (&wop, &wop);
|
|
sim_fpu_to32 (&ans, &wop);
|
|
result = ans;
|
|
break;
|
|
}
|
|
case fmt_double:
|
|
{
|
|
sim_fpu wop;
|
|
unsigned64 ans;
|
|
sim_fpu_64to (&wop, op);
|
|
sim_fpu_abs (&wop, &wop);
|
|
sim_fpu_to64 (&ans, &wop);
|
|
result = ans;
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
uword64
|
|
Negate(op,fmt)
|
|
uword64 op;
|
|
FP_formats fmt;
|
|
{
|
|
uword64 result = 0;
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Negate: %s: op = 0x%s\n",DOFMT(fmt),pr_addr(op));
|
|
#endif /* DEBUG */
|
|
|
|
/* The format type should already have been checked: */
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
{
|
|
sim_fpu wop;
|
|
unsigned32 ans;
|
|
sim_fpu_32to (&wop, op);
|
|
sim_fpu_neg (&wop, &wop);
|
|
sim_fpu_to32 (&ans, &wop);
|
|
result = ans;
|
|
break;
|
|
}
|
|
case fmt_double:
|
|
{
|
|
sim_fpu wop;
|
|
unsigned64 ans;
|
|
sim_fpu_64to (&wop, op);
|
|
sim_fpu_neg (&wop, &wop);
|
|
sim_fpu_to64 (&ans, &wop);
|
|
result = ans;
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
uword64
|
|
Add(op1,op2,fmt)
|
|
uword64 op1;
|
|
uword64 op2;
|
|
FP_formats fmt;
|
|
{
|
|
uword64 result = 0;
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Add: %s: op1 = 0x%s : op2 = 0x%s\n",DOFMT(fmt),pr_addr(op1),pr_addr(op2));
|
|
#endif /* DEBUG */
|
|
|
|
/* The registers must specify FPRs valid for operands of type
|
|
"fmt". If they are not valid, the result is undefined. */
|
|
|
|
/* The format type should already have been checked: */
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu ans;
|
|
unsigned32 res;
|
|
sim_fpu_32to (&wop1, op1);
|
|
sim_fpu_32to (&wop2, op2);
|
|
sim_fpu_add (&ans, &wop1, &wop2);
|
|
sim_fpu_to32 (&res, &ans);
|
|
result = res;
|
|
break;
|
|
}
|
|
case fmt_double:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu ans;
|
|
unsigned64 res;
|
|
sim_fpu_64to (&wop1, op1);
|
|
sim_fpu_64to (&wop2, op2);
|
|
sim_fpu_add (&ans, &wop1, &wop2);
|
|
sim_fpu_to64 (&res, &ans);
|
|
result = res;
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Add: returning 0x%s (format = %s)\n",pr_addr(result),DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
|
|
return(result);
|
|
}
|
|
|
|
uword64
|
|
Sub(op1,op2,fmt)
|
|
uword64 op1;
|
|
uword64 op2;
|
|
FP_formats fmt;
|
|
{
|
|
uword64 result = 0;
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Sub: %s: op1 = 0x%s : op2 = 0x%s\n",DOFMT(fmt),pr_addr(op1),pr_addr(op2));
|
|
#endif /* DEBUG */
|
|
|
|
/* The registers must specify FPRs valid for operands of type
|
|
"fmt". If they are not valid, the result is undefined. */
|
|
|
|
/* The format type should already have been checked: */
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu ans;
|
|
unsigned32 res;
|
|
sim_fpu_32to (&wop1, op1);
|
|
sim_fpu_32to (&wop2, op2);
|
|
sim_fpu_sub (&ans, &wop1, &wop2);
|
|
sim_fpu_to32 (&res, &ans);
|
|
result = res;
|
|
}
|
|
break;
|
|
case fmt_double:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu ans;
|
|
unsigned64 res;
|
|
sim_fpu_64to (&wop1, op1);
|
|
sim_fpu_64to (&wop2, op2);
|
|
sim_fpu_sub (&ans, &wop1, &wop2);
|
|
sim_fpu_to64 (&res, &ans);
|
|
result = res;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Sub: returning 0x%s (format = %s)\n",pr_addr(result),DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
|
|
return(result);
|
|
}
|
|
|
|
uword64
|
|
Multiply(op1,op2,fmt)
|
|
uword64 op1;
|
|
uword64 op2;
|
|
FP_formats fmt;
|
|
{
|
|
uword64 result = 0;
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Multiply: %s: op1 = 0x%s : op2 = 0x%s\n",DOFMT(fmt),pr_addr(op1),pr_addr(op2));
|
|
#endif /* DEBUG */
|
|
|
|
/* The registers must specify FPRs valid for operands of type
|
|
"fmt". If they are not valid, the result is undefined. */
|
|
|
|
/* The format type should already have been checked: */
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu ans;
|
|
unsigned32 res;
|
|
sim_fpu_32to (&wop1, op1);
|
|
sim_fpu_32to (&wop2, op2);
|
|
sim_fpu_mul (&ans, &wop1, &wop2);
|
|
sim_fpu_to32 (&res, &ans);
|
|
result = res;
|
|
break;
|
|
}
|
|
case fmt_double:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu ans;
|
|
unsigned64 res;
|
|
sim_fpu_64to (&wop1, op1);
|
|
sim_fpu_64to (&wop2, op2);
|
|
sim_fpu_mul (&ans, &wop1, &wop2);
|
|
sim_fpu_to64 (&res, &ans);
|
|
result = res;
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Multiply: returning 0x%s (format = %s)\n",pr_addr(result),DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
|
|
return(result);
|
|
}
|
|
|
|
uword64
|
|
Divide(op1,op2,fmt)
|
|
uword64 op1;
|
|
uword64 op2;
|
|
FP_formats fmt;
|
|
{
|
|
uword64 result = 0;
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Divide: %s: op1 = 0x%s : op2 = 0x%s\n",DOFMT(fmt),pr_addr(op1),pr_addr(op2));
|
|
#endif /* DEBUG */
|
|
|
|
/* The registers must specify FPRs valid for operands of type
|
|
"fmt". If they are not valid, the result is undefined. */
|
|
|
|
/* The format type should already have been checked: */
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu ans;
|
|
unsigned32 res;
|
|
sim_fpu_32to (&wop1, op1);
|
|
sim_fpu_32to (&wop2, op2);
|
|
sim_fpu_div (&ans, &wop1, &wop2);
|
|
sim_fpu_to32 (&res, &ans);
|
|
result = res;
|
|
break;
|
|
}
|
|
case fmt_double:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu ans;
|
|
unsigned64 res;
|
|
sim_fpu_64to (&wop1, op1);
|
|
sim_fpu_64to (&wop2, op2);
|
|
sim_fpu_div (&ans, &wop1, &wop2);
|
|
sim_fpu_to64 (&res, &ans);
|
|
result = res;
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Divide: returning 0x%s (format = %s)\n",pr_addr(result),DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
|
|
return(result);
|
|
}
|
|
|
|
uword64 UNUSED
|
|
Recip(op,fmt)
|
|
uword64 op;
|
|
FP_formats fmt;
|
|
{
|
|
uword64 result = 0;
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Recip: %s: op = 0x%s\n",DOFMT(fmt),pr_addr(op));
|
|
#endif /* DEBUG */
|
|
|
|
/* The registers must specify FPRs valid for operands of type
|
|
"fmt". If they are not valid, the result is undefined. */
|
|
|
|
/* The format type should already have been checked: */
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
{
|
|
sim_fpu wop;
|
|
sim_fpu ans;
|
|
unsigned32 res;
|
|
sim_fpu_32to (&wop, op);
|
|
sim_fpu_inv (&ans, &wop);
|
|
sim_fpu_to32 (&res, &ans);
|
|
result = res;
|
|
break;
|
|
}
|
|
case fmt_double:
|
|
{
|
|
sim_fpu wop;
|
|
sim_fpu ans;
|
|
unsigned64 res;
|
|
sim_fpu_64to (&wop, op);
|
|
sim_fpu_inv (&ans, &wop);
|
|
sim_fpu_to64 (&res, &ans);
|
|
result = res;
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Recip: returning 0x%s (format = %s)\n",pr_addr(result),DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
|
|
return(result);
|
|
}
|
|
|
|
uword64
|
|
SquareRoot(op,fmt)
|
|
uword64 op;
|
|
FP_formats fmt;
|
|
{
|
|
uword64 result = 0;
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: SquareRoot: %s: op = 0x%s\n",DOFMT(fmt),pr_addr(op));
|
|
#endif /* DEBUG */
|
|
|
|
/* The registers must specify FPRs valid for operands of type
|
|
"fmt". If they are not valid, the result is undefined. */
|
|
|
|
/* The format type should already have been checked: */
|
|
switch (fmt) {
|
|
case fmt_single:
|
|
{
|
|
sim_fpu wop;
|
|
sim_fpu ans;
|
|
unsigned32 res;
|
|
sim_fpu_32to (&wop, op);
|
|
sim_fpu_sqrt (&ans, &wop);
|
|
sim_fpu_to32 (&res, &ans);
|
|
result = res;
|
|
break;
|
|
}
|
|
case fmt_double:
|
|
{
|
|
sim_fpu wop;
|
|
sim_fpu ans;
|
|
unsigned64 res;
|
|
sim_fpu_64to (&wop, op);
|
|
sim_fpu_sqrt (&ans, &wop);
|
|
sim_fpu_to64 (&res, &ans);
|
|
result = res;
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: SquareRoot: returning 0x%s (format = %s)\n",pr_addr(result),DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
|
|
return(result);
|
|
}
|
|
|
|
#if 0
|
|
uword64
|
|
Max (uword64 op1,
|
|
uword64 op2,
|
|
FP_formats fmt)
|
|
{
|
|
int cmp;
|
|
unsigned64 result;
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Max: %s: op1 = 0x%s : op2 = 0x%s\n",DOFMT(fmt),pr_addr(op1),pr_addr(op2));
|
|
#endif /* DEBUG */
|
|
|
|
/* The registers must specify FPRs valid for operands of type
|
|
"fmt". If they are not valid, the result is undefined. */
|
|
|
|
/* The format type should already have been checked: */
|
|
switch (fmt)
|
|
{
|
|
case fmt_single:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu_32to (&wop1, op1);
|
|
sim_fpu_32to (&wop2, op2);
|
|
cmp = sim_fpu_cmp (&wop1, &wop2);
|
|
break;
|
|
}
|
|
case fmt_double:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu_64to (&wop1, op1);
|
|
sim_fpu_64to (&wop2, op2);
|
|
cmp = sim_fpu_cmp (&wop1, &wop2);
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
switch (cmp)
|
|
{
|
|
case SIM_FPU_IS_SNAN:
|
|
case SIM_FPU_IS_QNAN:
|
|
result = op1;
|
|
case SIM_FPU_IS_NINF:
|
|
case SIM_FPU_IS_NNUMBER:
|
|
case SIM_FPU_IS_NDENORM:
|
|
case SIM_FPU_IS_NZERO:
|
|
result = op2; /* op1 - op2 < 0 */
|
|
case SIM_FPU_IS_PINF:
|
|
case SIM_FPU_IS_PNUMBER:
|
|
case SIM_FPU_IS_PDENORM:
|
|
case SIM_FPU_IS_PZERO:
|
|
result = op1; /* op1 - op2 > 0 */
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Max: returning 0x%s (format = %s)\n",pr_addr(result),DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
|
|
return(result);
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
uword64
|
|
Min (uword64 op1,
|
|
uword64 op2,
|
|
FP_formats fmt)
|
|
{
|
|
int cmp;
|
|
unsigned64 result;
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Min: %s: op1 = 0x%s : op2 = 0x%s\n",DOFMT(fmt),pr_addr(op1),pr_addr(op2));
|
|
#endif /* DEBUG */
|
|
|
|
/* The registers must specify FPRs valid for operands of type
|
|
"fmt". If they are not valid, the result is undefined. */
|
|
|
|
/* The format type should already have been checked: */
|
|
switch (fmt)
|
|
{
|
|
case fmt_single:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu_32to (&wop1, op1);
|
|
sim_fpu_32to (&wop2, op2);
|
|
cmp = sim_fpu_cmp (&wop1, &wop2);
|
|
break;
|
|
}
|
|
case fmt_double:
|
|
{
|
|
sim_fpu wop1;
|
|
sim_fpu wop2;
|
|
sim_fpu_64to (&wop1, op1);
|
|
sim_fpu_64to (&wop2, op2);
|
|
cmp = sim_fpu_cmp (&wop1, &wop2);
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
switch (cmp)
|
|
{
|
|
case SIM_FPU_IS_SNAN:
|
|
case SIM_FPU_IS_QNAN:
|
|
result = op1;
|
|
case SIM_FPU_IS_NINF:
|
|
case SIM_FPU_IS_NNUMBER:
|
|
case SIM_FPU_IS_NDENORM:
|
|
case SIM_FPU_IS_NZERO:
|
|
result = op1; /* op1 - op2 < 0 */
|
|
case SIM_FPU_IS_PINF:
|
|
case SIM_FPU_IS_PNUMBER:
|
|
case SIM_FPU_IS_PDENORM:
|
|
case SIM_FPU_IS_PZERO:
|
|
result = op2; /* op1 - op2 > 0 */
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Min: returning 0x%s (format = %s)\n",pr_addr(result),DOFMT(fmt));
|
|
#endif /* DEBUG */
|
|
|
|
return(result);
|
|
}
|
|
#endif
|
|
|
|
uword64
|
|
convert (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
address_word cia,
|
|
int rm,
|
|
uword64 op,
|
|
FP_formats from,
|
|
FP_formats to)
|
|
{
|
|
sim_fpu wop;
|
|
sim_fpu_round round;
|
|
unsigned32 result32;
|
|
unsigned64 result64;
|
|
|
|
#ifdef DEBUG
|
|
#if 0 /* FIXME: doesn't compile */
|
|
printf("DBG: Convert: mode %s : op 0x%s : from %s : to %s : (PC = 0x%s)\n",RMMODE(rm),pr_addr(op),DOFMT(from),DOFMT(to),pr_addr(IPC));
|
|
#endif
|
|
#endif /* DEBUG */
|
|
|
|
switch (rm)
|
|
{
|
|
case FP_RM_NEAREST:
|
|
/* Round result to nearest representable value. When two
|
|
representable values are equally near, round to the value
|
|
that has a least significant bit of zero (i.e. is even). */
|
|
round = sim_fpu_round_near;
|
|
break;
|
|
case FP_RM_TOZERO:
|
|
/* Round result to the value closest to, and not greater in
|
|
magnitude than, the result. */
|
|
round = sim_fpu_round_zero;
|
|
break;
|
|
case FP_RM_TOPINF:
|
|
/* Round result to the value closest to, and not less than,
|
|
the result. */
|
|
round = sim_fpu_round_up;
|
|
break;
|
|
|
|
case FP_RM_TOMINF:
|
|
/* Round result to the value closest to, and not greater than,
|
|
the result. */
|
|
round = sim_fpu_round_down;
|
|
break;
|
|
default:
|
|
round = 0;
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
/* Convert the input to sim_fpu internal format */
|
|
switch (from)
|
|
{
|
|
case fmt_double:
|
|
sim_fpu_64to (&wop, op);
|
|
break;
|
|
case fmt_single:
|
|
sim_fpu_32to (&wop, op);
|
|
break;
|
|
case fmt_word:
|
|
sim_fpu_i32to (&wop, op, round);
|
|
break;
|
|
case fmt_long:
|
|
sim_fpu_i64to (&wop, op, round);
|
|
break;
|
|
default:
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
/* Convert sim_fpu format into the output */
|
|
/* The value WOP is converted to the destination format, rounding
|
|
using mode RM. When the destination is a fixed-point format, then
|
|
a source value of Infinity, NaN or one which would round to an
|
|
integer outside the fixed point range then an IEEE Invalid
|
|
Operation condition is raised. */
|
|
switch (to)
|
|
{
|
|
case fmt_single:
|
|
sim_fpu_round_32 (&wop, round, 0);
|
|
sim_fpu_to32 (&result32, &wop);
|
|
result64 = result32;
|
|
break;
|
|
case fmt_double:
|
|
sim_fpu_round_64 (&wop, round, 0);
|
|
sim_fpu_to64 (&result64, &wop);
|
|
break;
|
|
case fmt_word:
|
|
sim_fpu_to32i (&result32, &wop, round);
|
|
result64 = result32;
|
|
break;
|
|
case fmt_long:
|
|
sim_fpu_to64i (&result64, &wop, round);
|
|
break;
|
|
default:
|
|
result64 = 0;
|
|
fprintf (stderr, "Bad switch\n");
|
|
abort ();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: Convert: returning 0x%s (to format = %s)\n",pr_addr(result64),DOFMT(to));
|
|
#endif /* DEBUG */
|
|
|
|
return(result64);
|
|
}
|
|
|
|
|
|
/*-- co-processor support routines ------------------------------------------*/
|
|
|
|
static int UNUSED
|
|
CoProcPresent(unsigned int coproc_number)
|
|
{
|
|
/* Return TRUE if simulator provides a model for the given co-processor number */
|
|
return(0);
|
|
}
|
|
|
|
void
|
|
cop_lw (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
address_word cia,
|
|
int coproc_num,
|
|
int coproc_reg,
|
|
unsigned int memword)
|
|
{
|
|
switch (coproc_num)
|
|
{
|
|
case 1:
|
|
if (CURRENT_FLOATING_POINT == HARD_FLOATING_POINT)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("DBG: COP_LW: memword = 0x%08X (uword64)memword = 0x%s\n",memword,pr_addr(memword));
|
|
#endif
|
|
StoreFPR(coproc_reg,fmt_word,(uword64)memword);
|
|
FPR_STATE[coproc_reg] = fmt_uninterpreted;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
#if 0 /* this should be controlled by a configuration option */
|
|
sim_io_printf(sd,"COP_LW(%d,%d,0x%08X) at PC = 0x%s : TODO (architecture specific)\n",coproc_num,coproc_reg,memword,pr_addr(cia));
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
cop_ld (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
address_word cia,
|
|
int coproc_num,
|
|
int coproc_reg,
|
|
uword64 memword)
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
printf("DBG: COP_LD: coproc_num = %d, coproc_reg = %d, value = 0x%s : PC = 0x%s\n", coproc_num, coproc_reg, pr_uword64(memword), pr_addr(cia) );
|
|
#endif
|
|
|
|
switch (coproc_num) {
|
|
case 1:
|
|
if (CURRENT_FLOATING_POINT == HARD_FLOATING_POINT)
|
|
{
|
|
StoreFPR(coproc_reg,fmt_uninterpreted,memword);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
#if 0 /* this message should be controlled by a configuration option */
|
|
sim_io_printf(sd,"COP_LD(%d,%d,0x%s) at PC = 0x%s : TODO (architecture specific)\n",coproc_num,coproc_reg,pr_addr(memword),pr_addr(cia));
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
cop_sw (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
address_word cia,
|
|
int coproc_num,
|
|
int coproc_reg)
|
|
{
|
|
unsigned int value = 0;
|
|
|
|
switch (coproc_num)
|
|
{
|
|
case 1:
|
|
if (CURRENT_FLOATING_POINT == HARD_FLOATING_POINT)
|
|
{
|
|
FP_formats hold;
|
|
hold = FPR_STATE[coproc_reg];
|
|
FPR_STATE[coproc_reg] = fmt_word;
|
|
value = (unsigned int)ValueFPR(coproc_reg,fmt_uninterpreted);
|
|
FPR_STATE[coproc_reg] = hold;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
#if 0 /* should be controlled by configuration option */
|
|
sim_io_printf(sd,"COP_SW(%d,%d) at PC = 0x%s : TODO (architecture specific)\n",coproc_num,coproc_reg,pr_addr(cia));
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
return(value);
|
|
}
|
|
|
|
uword64
|
|
cop_sd (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
address_word cia,
|
|
int coproc_num,
|
|
int coproc_reg)
|
|
{
|
|
uword64 value = 0;
|
|
switch (coproc_num)
|
|
{
|
|
case 1:
|
|
if (CURRENT_FLOATING_POINT == HARD_FLOATING_POINT)
|
|
{
|
|
value = ValueFPR(coproc_reg,fmt_uninterpreted);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
#if 0 /* should be controlled by configuration option */
|
|
sim_io_printf(sd,"COP_SD(%d,%d) at PC = 0x%s : TODO (architecture specific)\n",coproc_num,coproc_reg,pr_addr(cia));
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
return(value);
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
decode_coproc (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
address_word cia,
|
|
unsigned int instruction)
|
|
{
|
|
int coprocnum = ((instruction >> 26) & 3);
|
|
|
|
switch (coprocnum)
|
|
{
|
|
case 0: /* standard CPU control and cache registers */
|
|
{
|
|
int code = ((instruction >> 21) & 0x1F);
|
|
int rt = ((instruction >> 16) & 0x1F);
|
|
int rd = ((instruction >> 11) & 0x1F);
|
|
int tail = instruction & 0x3ff;
|
|
/* R4000 Users Manual (second edition) lists the following CP0
|
|
instructions:
|
|
CODE><-RT><RD-><--TAIL--->
|
|
DMFC0 Doubleword Move From CP0 (VR4100 = 01000000001tttttddddd00000000000)
|
|
DMTC0 Doubleword Move To CP0 (VR4100 = 01000000101tttttddddd00000000000)
|
|
MFC0 word Move From CP0 (VR4100 = 01000000000tttttddddd00000000000)
|
|
MTC0 word Move To CP0 (VR4100 = 01000000100tttttddddd00000000000)
|
|
TLBR Read Indexed TLB Entry (VR4100 = 01000010000000000000000000000001)
|
|
TLBWI Write Indexed TLB Entry (VR4100 = 01000010000000000000000000000010)
|
|
TLBWR Write Random TLB Entry (VR4100 = 01000010000000000000000000000110)
|
|
TLBP Probe TLB for Matching Entry (VR4100 = 01000010000000000000000000001000)
|
|
CACHE Cache operation (VR4100 = 101111bbbbbpppppiiiiiiiiiiiiiiii)
|
|
ERET Exception return (VR4100 = 01000010000000000000000000011000)
|
|
*/
|
|
if (((code == 0x00) || (code == 0x04)) && tail == 0)
|
|
{
|
|
/* M[TF]C0 - 32 bit word */
|
|
|
|
switch (rd) /* NOTEs: Standard CP0 registers */
|
|
{
|
|
/* 0 = Index R4000 VR4100 VR4300 */
|
|
/* 1 = Random R4000 VR4100 VR4300 */
|
|
/* 2 = EntryLo0 R4000 VR4100 VR4300 */
|
|
/* 3 = EntryLo1 R4000 VR4100 VR4300 */
|
|
/* 4 = Context R4000 VR4100 VR4300 */
|
|
/* 5 = PageMask R4000 VR4100 VR4300 */
|
|
/* 6 = Wired R4000 VR4100 VR4300 */
|
|
/* 8 = BadVAddr R4000 VR4100 VR4300 */
|
|
/* 9 = Count R4000 VR4100 VR4300 */
|
|
/* 10 = EntryHi R4000 VR4100 VR4300 */
|
|
/* 11 = Compare R4000 VR4100 VR4300 */
|
|
/* 12 = SR R4000 VR4100 VR4300 */
|
|
#ifdef SUBTARGET_R3900
|
|
case 3:
|
|
/* 3 = Config R3900 */
|
|
case 7:
|
|
/* 7 = Cache R3900 */
|
|
case 15:
|
|
/* 15 = PRID R3900 */
|
|
|
|
/* ignore */
|
|
break;
|
|
|
|
case 8:
|
|
/* 8 = BadVAddr R4000 VR4100 VR4300 */
|
|
if (code == 0x00)
|
|
GPR[rt] = COP0_BADVADDR;
|
|
else
|
|
COP0_BADVADDR = GPR[rt];
|
|
break;
|
|
|
|
#endif /* SUBTARGET_R3900 */
|
|
case 12:
|
|
if (code == 0x00)
|
|
GPR[rt] = SR;
|
|
else
|
|
SR = GPR[rt];
|
|
break;
|
|
/* 13 = Cause R4000 VR4100 VR4300 */
|
|
case 13:
|
|
if (code == 0x00)
|
|
GPR[rt] = CAUSE;
|
|
else
|
|
CAUSE = GPR[rt];
|
|
break;
|
|
/* 14 = EPC R4000 VR4100 VR4300 */
|
|
case 14:
|
|
if (code == 0x00)
|
|
GPR[rt] = (signed_word) (signed_address) EPC;
|
|
else
|
|
EPC = GPR[rt];
|
|
break;
|
|
/* 15 = PRId R4000 VR4100 VR4300 */
|
|
#ifdef SUBTARGET_R3900
|
|
/* 16 = Debug */
|
|
case 16:
|
|
if (code == 0x00)
|
|
GPR[rt] = Debug;
|
|
else
|
|
Debug = GPR[rt];
|
|
break;
|
|
#else
|
|
/* 16 = Config R4000 VR4100 VR4300 */
|
|
case 16:
|
|
if (code == 0x00)
|
|
GPR[rt] = C0_CONFIG;
|
|
else
|
|
C0_CONFIG = GPR[rt];
|
|
break;
|
|
#endif
|
|
#ifdef SUBTARGET_R3900
|
|
/* 17 = Debug */
|
|
case 17:
|
|
if (code == 0x00)
|
|
GPR[rt] = DEPC;
|
|
else
|
|
DEPC = GPR[rt];
|
|
break;
|
|
#else
|
|
/* 17 = LLAddr R4000 VR4100 VR4300 */
|
|
#endif
|
|
/* 18 = WatchLo R4000 VR4100 VR4300 */
|
|
/* 19 = WatchHi R4000 VR4100 VR4300 */
|
|
/* 20 = XContext R4000 VR4100 VR4300 */
|
|
/* 26 = PErr or ECC R4000 VR4100 VR4300 */
|
|
/* 27 = CacheErr R4000 VR4100 */
|
|
/* 28 = TagLo R4000 VR4100 VR4300 */
|
|
/* 29 = TagHi R4000 VR4100 VR4300 */
|
|
/* 30 = ErrorEPC R4000 VR4100 VR4300 */
|
|
GPR[rt] = 0xDEADC0DE; /* CPR[0,rd] */
|
|
/* CPR[0,rd] = GPR[rt]; */
|
|
default:
|
|
if (code == 0x00)
|
|
GPR[rt] = (signed_word) (signed32) COP0_GPR[rd];
|
|
else
|
|
COP0_GPR[rd] = GPR[rt];
|
|
#if 0
|
|
if (code == 0x00)
|
|
sim_io_printf(sd,"Warning: MFC0 %d,%d ignored, PC=%08x (architecture specific)\n",rt,rd, (unsigned)cia);
|
|
else
|
|
sim_io_printf(sd,"Warning: MTC0 %d,%d ignored, PC=%08x (architecture specific)\n",rt,rd, (unsigned)cia);
|
|
#endif
|
|
}
|
|
}
|
|
else if (code == 0x10 && (tail & 0x3f) == 0x18)
|
|
{
|
|
/* ERET */
|
|
if (SR & status_ERL)
|
|
{
|
|
/* Oops, not yet available */
|
|
sim_io_printf(sd,"Warning: ERET when SR[ERL] set not handled yet");
|
|
PC = EPC;
|
|
SR &= ~status_ERL;
|
|
}
|
|
else
|
|
{
|
|
PC = EPC;
|
|
SR &= ~status_EXL;
|
|
}
|
|
}
|
|
else if (code == 0x10 && (tail & 0x3f) == 0x10)
|
|
{
|
|
/* RFE */
|
|
#ifdef SUBTARGET_R3900
|
|
/* TX39: Copy IEp/KUp -> IEc/KUc, and IEo/KUo -> IEp/KUp */
|
|
|
|
/* shift IE/KU history bits right */
|
|
SR = LSMASKED32(SR, 31, 4) | LSINSERTED32(LSEXTRACTED32(SR, 5, 2), 3, 0);
|
|
|
|
/* TODO: CACHE register */
|
|
#endif /* SUBTARGET_R3900 */
|
|
}
|
|
else if (code == 0x10 && (tail & 0x3f) == 0x1F)
|
|
{
|
|
/* DERET */
|
|
Debug &= ~Debug_DM;
|
|
DELAYSLOT();
|
|
DSPC = DEPC;
|
|
}
|
|
else
|
|
sim_io_eprintf(sd,"Unrecognised COP0 instruction 0x%08X at PC = 0x%s : No handler present\n",instruction,pr_addr(cia));
|
|
/* TODO: When executing an ERET or RFE instruction we should
|
|
clear LLBIT, to ensure that any out-standing atomic
|
|
read/modify/write sequence fails. */
|
|
}
|
|
break;
|
|
|
|
case 2: /* co-processor 2 */
|
|
{
|
|
int handle = 0;
|
|
|
|
|
|
if(! handle)
|
|
{
|
|
sim_io_eprintf(sd, "COP2 instruction 0x%08X at PC = 0x%s : No handler present\n",
|
|
instruction,pr_addr(cia));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 1: /* should not occur (FPU co-processor) */
|
|
case 3: /* should not occur (FPU co-processor) */
|
|
SignalException(ReservedInstruction,instruction);
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* This code copied from gdb's utils.c. Would like to share this code,
|
|
but don't know of a common place where both could get to it. */
|
|
|
|
/* Temporary storage using circular buffer */
|
|
#define NUMCELLS 16
|
|
#define CELLSIZE 32
|
|
static char*
|
|
get_cell (void)
|
|
{
|
|
static char buf[NUMCELLS][CELLSIZE];
|
|
static int cell=0;
|
|
if (++cell>=NUMCELLS) cell=0;
|
|
return buf[cell];
|
|
}
|
|
|
|
/* Print routines to handle variable size regs, etc */
|
|
|
|
/* Eliminate warning from compiler on 32-bit systems */
|
|
static int thirty_two = 32;
|
|
|
|
char*
|
|
pr_addr(addr)
|
|
SIM_ADDR addr;
|
|
{
|
|
char *paddr_str=get_cell();
|
|
switch (sizeof(addr))
|
|
{
|
|
case 8:
|
|
sprintf(paddr_str,"%08lx%08lx",
|
|
(unsigned long)(addr>>thirty_two),(unsigned long)(addr&0xffffffff));
|
|
break;
|
|
case 4:
|
|
sprintf(paddr_str,"%08lx",(unsigned long)addr);
|
|
break;
|
|
case 2:
|
|
sprintf(paddr_str,"%04x",(unsigned short)(addr&0xffff));
|
|
break;
|
|
default:
|
|
sprintf(paddr_str,"%x",addr);
|
|
}
|
|
return paddr_str;
|
|
}
|
|
|
|
char*
|
|
pr_uword64(addr)
|
|
uword64 addr;
|
|
{
|
|
char *paddr_str=get_cell();
|
|
sprintf(paddr_str,"%08lx%08lx",
|
|
(unsigned long)(addr>>thirty_two),(unsigned long)(addr&0xffffffff));
|
|
return paddr_str;
|
|
}
|
|
|
|
|
|
void
|
|
mips_core_signal (SIM_DESC sd,
|
|
sim_cpu *cpu,
|
|
sim_cia cia,
|
|
unsigned map,
|
|
int nr_bytes,
|
|
address_word addr,
|
|
transfer_type transfer,
|
|
sim_core_signals sig)
|
|
{
|
|
const char *copy = (transfer == read_transfer ? "read" : "write");
|
|
address_word ip = CIA_ADDR (cia);
|
|
|
|
switch (sig)
|
|
{
|
|
case sim_core_unmapped_signal:
|
|
sim_io_eprintf (sd, "mips-core: %d byte %s to unmapped address 0x%lx at 0x%lx\n",
|
|
nr_bytes, copy,
|
|
(unsigned long) addr, (unsigned long) ip);
|
|
COP0_BADVADDR = addr;
|
|
SignalExceptionDataReference();
|
|
break;
|
|
|
|
case sim_core_unaligned_signal:
|
|
sim_io_eprintf (sd, "mips-core: %d byte %s to unaligned address 0x%lx at 0x%lx\n",
|
|
nr_bytes, copy,
|
|
(unsigned long) addr, (unsigned long) ip);
|
|
COP0_BADVADDR = addr;
|
|
if(transfer == read_transfer)
|
|
SignalExceptionAddressLoad();
|
|
else
|
|
SignalExceptionAddressStore();
|
|
break;
|
|
|
|
default:
|
|
sim_engine_abort (sd, cpu, cia,
|
|
"mips_core_signal - internal error - bad switch");
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
mips_cpu_exception_trigger(SIM_DESC sd, sim_cpu* cpu, address_word cia)
|
|
{
|
|
ASSERT(cpu != NULL);
|
|
|
|
if(cpu->exc_suspended > 0)
|
|
sim_io_eprintf(sd, "Warning, nested exception triggered (%d)\n", cpu->exc_suspended);
|
|
|
|
PC = cia;
|
|
memcpy(cpu->exc_trigger_registers, cpu->registers, sizeof(cpu->exc_trigger_registers));
|
|
cpu->exc_suspended = 0;
|
|
}
|
|
|
|
void
|
|
mips_cpu_exception_suspend(SIM_DESC sd, sim_cpu* cpu, int exception)
|
|
{
|
|
ASSERT(cpu != NULL);
|
|
|
|
if(cpu->exc_suspended > 0)
|
|
sim_io_eprintf(sd, "Warning, nested exception signal (%d then %d)\n",
|
|
cpu->exc_suspended, exception);
|
|
|
|
memcpy(cpu->exc_suspend_registers, cpu->registers, sizeof(cpu->exc_suspend_registers));
|
|
memcpy(cpu->registers, cpu->exc_trigger_registers, sizeof(cpu->registers));
|
|
cpu->exc_suspended = exception;
|
|
}
|
|
|
|
void
|
|
mips_cpu_exception_resume(SIM_DESC sd, sim_cpu* cpu, int exception)
|
|
{
|
|
ASSERT(cpu != NULL);
|
|
|
|
if(exception == 0 && cpu->exc_suspended > 0)
|
|
{
|
|
/* warn not for breakpoints */
|
|
if(cpu->exc_suspended != sim_signal_to_host(sd, SIM_SIGTRAP))
|
|
sim_io_eprintf(sd, "Warning, resuming but ignoring pending exception signal (%d)\n",
|
|
cpu->exc_suspended);
|
|
}
|
|
else if(exception != 0 && cpu->exc_suspended > 0)
|
|
{
|
|
if(exception != cpu->exc_suspended)
|
|
sim_io_eprintf(sd, "Warning, resuming with mismatched exception signal (%d vs %d)\n",
|
|
cpu->exc_suspended, exception);
|
|
|
|
memcpy(cpu->registers, cpu->exc_suspend_registers, sizeof(cpu->registers));
|
|
}
|
|
else if(exception != 0 && cpu->exc_suspended == 0)
|
|
{
|
|
sim_io_eprintf(sd, "Warning, ignoring spontanous exception signal (%d)\n", exception);
|
|
}
|
|
cpu->exc_suspended = 0;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/*> EOF interp.c <*/
|