/* Target-dependent code for the or1k architecture, for GDB, the GNU Debugger.
|
/* Target-dependent code for the or1k architecture, for GDB, the GNU Debugger.
|
|
|
Copyright 1988-1999, Free Software Foundation, Inc.
|
Copyright 1988-1999, Free Software Foundation, Inc.
|
Contributed by Alessandro Forin(af@cs.cmu.edu at CMU
|
Contributed by Alessandro Forin(af@cs.cmu.edu at CMU
|
and by Per Bothner(bothner@cs.wisc.edu) at U.Wisconsin.
|
and by Per Bothner(bothner@cs.wisc.edu) at U.Wisconsin.
|
|
|
This file is part of GDB.
|
This file is part of GDB.
|
|
|
This program is free software; you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
the Free Software Foundation; either version 2 of the License, or
|
the Free Software Foundation; either version 2 of the License, or
|
(at your option) any later version.
|
(at your option) any later version.
|
|
|
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
GNU General Public License for more details.
|
GNU General Public License for more details.
|
|
|
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
Foundation, Inc., 59 Temple Place - Suite 330,
|
Foundation, Inc., 59 Temple Place - Suite 330,
|
Boston, MA 02111-1307, USA. */
|
Boston, MA 02111-1307, USA. */
|
|
|
#include "demangle.h"
|
#include "demangle.h"
|
#include "defs.h"
|
#include "defs.h"
|
#include "gdb_string.h"
|
#include "gdb_string.h"
|
#include "frame.h"
|
#include "frame.h"
|
#include "inferior.h"
|
#include "inferior.h"
|
#include "symtab.h"
|
#include "symtab.h"
|
#include "value.h"
|
#include "value.h"
|
#include "gdbcmd.h"
|
#include "gdbcmd.h"
|
#include "language.h"
|
#include "language.h"
|
#include "gdbcore.h"
|
#include "gdbcore.h"
|
#include "symfile.h"
|
#include "symfile.h"
|
#include "objfiles.h"
|
#include "objfiles.h"
|
#include "gdbtypes.h"
|
#include "gdbtypes.h"
|
#include "target.h"
|
#include "target.h"
|
|
|
#include "opcode/or32.h"
|
#include "opcode/or32.h"
|
|
|
/* *INDENT-OFF* */
|
/* *INDENT-OFF* */
|
|
|
/* Group reg name size. See or1k_reg_names. */
|
/* Group reg name size. See or1k_reg_names. */
|
int or1k_group_name_sizes[OR1K_NUM_SPR_GROUPS] = {
|
int or1k_group_name_sizes[OR1K_NUM_SPR_GROUPS] = {
|
80, 0, 0, 6, 4, 3,
|
80, 0, 0, 6, 4, 3,
|
22, 16, 1, 3, 2, 8
|
22, 16, 1, 3, 2, 8
|
};
|
};
|
|
|
/* Generated reg names (max valid alias index).
|
/* Generated reg names (max valid alias index).
|
See or1k_spr_reg_name. */
|
See or1k_spr_reg_name. */
|
int or1k_spr_valid_aliases[OR1K_NUM_SPR_GROUPS] = {
|
int or1k_spr_valid_aliases[OR1K_NUM_SPR_GROUPS] = {
|
2047+1, 2047+1, 2047+1, 258+1, 257+1, 257+1,
|
2047+1, 2047+1, 2047+1, 258+1, 257+1, 257+1,
|
78+1, 263+1, 16+1, 18+1, 256+1, 7+1
|
78+1, 263+1, 16+1, 18+1, 256+1, 7+1
|
};
|
};
|
|
|
/* Register names. */
|
/* Register names. */
|
char *or1k_reg_names[] = {
|
char *or1k_reg_names[] = {
|
|
|
/* group 0 - general*/
|
/* group 0 - general*/
|
"VR", "UPR", "CPUCFGR", "DMMUCFGR", "IMMUCFGR", "DCCFGR", "ICCFGR", "DCFGR",
|
"VR", "UPR", "CPUCFGR", "DMMUCFGR", "IMMUCFGR", "DCCFGR", "ICCFGR", "DCFGR",
|
"PCCFGR", "SPR0_9", "SPR0_10", "SPR0_11", "SPR0_12", "SPR0_13", "SPR0_14", "SPR0_15",
|
"PCCFGR", "SPR0_9", "SPR0_10", "SPR0_11", "SPR0_12", "SPR0_13", "SPR0_14", "SPR0_15",
|
"NPC", "SR", "PPC", "SPR0_19", "SPR0_20", "SPR0_21", "SPR0_22", "SPR0_23",
|
"NPC", "SR", "PPC", "SPR0_19", "SPR0_20", "SPR0_21", "SPR0_22", "SPR0_23",
|
"SPR0_24", "SPR0_25", "SPR0_26", "SPR0_27", "SPR0_28", "SPR0_29", "SPR0_30", "SPR0_31",
|
"SPR0_24", "SPR0_25", "SPR0_26", "SPR0_27", "SPR0_28", "SPR0_29", "SPR0_30", "SPR0_31",
|
"EPCR0", "EPCR1", "EPCR2", "EPCR3", "EPCR4", "EPCR5", "EPCR6", "EPCR7",
|
"EPCR0", "EPCR1", "EPCR2", "EPCR3", "EPCR4", "EPCR5", "EPCR6", "EPCR7",
|
"EPCR8", "EPCR9", "EPCR10", "EPCR11", "EPCR12", "EPCR13", "EPCR14", "EPCR15",
|
"EPCR8", "EPCR9", "EPCR10", "EPCR11", "EPCR12", "EPCR13", "EPCR14", "EPCR15",
|
"EEAR0","EEAR1", "EEAR2", "EEAR3", "EEAR4", "EEAR5", "EEAR6", "EEAR7",
|
"EEAR0","EEAR1", "EEAR2", "EEAR3", "EEAR4", "EEAR5", "EEAR6", "EEAR7",
|
"EEAR8", "EEAR9", "EEAR10", "EEAR11", "EEAR12", "EEAR13", "EEAR14", "EEAR15",
|
"EEAR8", "EEAR9", "EEAR10", "EEAR11", "EEAR12", "EEAR13", "EEAR14", "EEAR15",
|
"ESR0", "ESR1", "ESR2", "ESR3", "ESR4", "ESR5", "ESR6", "ESR7",
|
"ESR0", "ESR1", "ESR2", "ESR3", "ESR4", "ESR5", "ESR6", "ESR7",
|
"ESR8", "ESR9", "ESR10", "ESR11", "ESR12", "ESR13", "ESR14", "ESR15",
|
"ESR8", "ESR9", "ESR10", "ESR11", "ESR12", "ESR13", "ESR14", "ESR15",
|
|
|
/* gpr+vf registers generated */
|
/* gpr+vf registers generated */
|
/* group 1 - Data MMU - not listed, generated */
|
/* group 1 - Data MMU - not listed, generated */
|
/* group 2 - Instruction MMU - not listed, generated */
|
/* group 2 - Instruction MMU - not listed, generated */
|
|
|
/* group 3 - Data cache */
|
/* group 3 - Data cache */
|
"DCCR", "DCBPR", "DCBFR", "DCBIR", "DCBWR", "DCBLR",
|
"DCCR", "DCBPR", "DCBFR", "DCBIR", "DCBWR", "DCBLR",
|
|
|
/* group 4 - Instruction cache */
|
/* group 4 - Instruction cache */
|
"ICCR", "ICBPR", "ICBIR", "ICBLR",
|
"ICCR", "ICBPR", "ICBIR", "ICBLR",
|
|
|
/* group 5 - MAC */
|
/* group 5 - MAC */
|
"SPR5_0", "MACLO", "MACHI",
|
"SPR5_0", "MACLO", "MACHI",
|
|
|
/* group 6 - debug */
|
/* group 6 - debug */
|
"DVR0", "DVR1", "DVR2", "DVR3", "DVR4", "DVR5", "DVR6", "DVR7",
|
"DVR0", "DVR1", "DVR2", "DVR3", "DVR4", "DVR5", "DVR6", "DVR7",
|
"DCR0", "DCR1", "DCR2", "DCR3", "DCR4", "DCR5", "DCR6", "DCR7",
|
"DCR0", "DCR1", "DCR2", "DCR3", "DCR4", "DCR5", "DCR6", "DCR7",
|
"DMR1", "DMR2", "DCWR0","DCWR1","DSR", "DRR",
|
"DMR1", "DMR2", "DCWR0","DCWR1","DSR", "DRR",
|
|
|
/* group 7 - performance counters unit */
|
/* group 7 - performance counters unit */
|
"PCCR0", "PCCR1", "PCCR2", "PCCR3", "PCCR4", "PCCR5", "PCCR6", "PCCR7",
|
"PCCR0", "PCCR1", "PCCR2", "PCCR3", "PCCR4", "PCCR5", "PCCR6", "PCCR7",
|
"PCCMR0", "PCMR1", "PCMR2", "PCMR3", "PCMR4", "PCMR5", "PCMR6", "PCMR7",
|
"PCCMR0", "PCMR1", "PCMR2", "PCMR3", "PCMR4", "PCMR5", "PCMR6", "PCMR7",
|
|
|
/* group 8 - power management */
|
/* group 8 - power management */
|
"PMR",
|
"PMR",
|
|
|
/* group 9 - PIC */
|
/* group 9 - PIC */
|
"PICMR", /* now-defunct PICPR */ "SPR9_1", "PICSR",
|
"PICMR", /* now-defunct PICPR */ "SPR9_1", "PICSR",
|
/* group 10 - tick timer */
|
/* group 10 - tick timer */
|
"TTMR", "TTCR",
|
"TTMR", "TTCR",
|
|
|
/* group 11 - configuration */
|
/* group 11 - configuration */
|
"CPUCFGR", "DMMUCFGR", "IMMUCFGR", "DCCFGR", "ICCFGR", "SPR11_5", "DCFGR", "PCCFGR"
|
"CPUCFGR", "DMMUCFGR", "IMMUCFGR", "DCCFGR", "ICCFGR", "SPR11_5", "DCFGR", "PCCFGR"
|
};
|
};
|
|
|
static char *or1k_gdb_reg_names[] = {
|
static char *or1k_gdb_reg_names[] = {
|
|
|
/* general purpose registers */
|
/* general purpose registers */
|
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",
|
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",
|
"R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
|
"R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
|
"R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23",
|
"R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23",
|
"R24", "R25", "R26", "R27", "R28", "R29", "R30", "R31",
|
"R24", "R25", "R26", "R27", "R28", "R29", "R30", "R31",
|
|
|
/* Modified by CZ 12/09/01 */
|
/* Modified by CZ 12/09/01 */
|
/* vector/floating point registers */
|
/* vector/floating point registers */
|
"VFA0", "VFA1", "VFA2", "VFA3", "VFA4", "VFA5", "VFRV ", "VFR7",
|
"VFA0", "VFA1", "VFA2", "VFA3", "VFA4", "VFA5", "VFRV ", "VFR7",
|
"VFR8", "VFR9", "VFR10", "VFR11", "VFR12", "VFR13", "VFR14", "VFR15",
|
"VFR8", "VFR9", "VFR10", "VFR11", "VFR12", "VFR13", "VFR14", "VFR15",
|
"VFR16", "VFR17", "VFR18", "VFR19", "VFR20", "VFR21", "VFR22", "VFR23",
|
"VFR16", "VFR17", "VFR18", "VFR19", "VFR20", "VFR21", "VFR22", "VFR23",
|
"VFR24", "VFR25", "VFR26", "VFR27", "VFR28", "VFR29", "VFR30", "VFR31",
|
"VFR24", "VFR25", "VFR26", "VFR27", "VFR28", "VFR29", "VFR30", "VFR31",
|
"PC", "SR", "EPCR", "ExPC", "ExEAR", "ExSR"
|
"PC", "SR", "EPCR", "ExPC", "ExEAR", "ExSR"
|
};
|
};
|
|
|
static char *or1k_group_names[] = {
|
static char *or1k_group_names[] = {
|
"SYS", "DMMU", "IMMU", "DCACHE", "ICACHE", "MAC", "DEBUG", "PERF",
|
"SYS", "DMMU", "IMMU", "DCACHE", "ICACHE", "MAC", "DEBUG", "PERF",
|
"POWER", "PIC", "TIMER", "CONFIG"
|
"POWER", "PIC", "TIMER", "CONFIG"
|
};
|
};
|
|
|
/* Table of or1k signals. */
|
/* Table of or1k signals. */
|
static struct
|
static struct
|
{
|
{
|
char *name;
|
char *name;
|
char *string;
|
char *string;
|
} or1k_signals [NUM_OR1K_SIGNALS + 1] =
|
} or1k_signals [NUM_OR1K_SIGNALS + 1] =
|
{
|
{
|
{"RSTE", "Reset Exception"},
|
{"RSTE", "Reset Exception"},
|
{"BUSE", "Bus Error" },
|
{"BUSE", "Bus Error" },
|
{"DFPE", "Data Page Fault Exception"},
|
{"DFPE", "Data Page Fault Exception"},
|
{"IFPE", "Instruction Page Fault Exception"},
|
{"IFPE", "Instruction Page Fault Exception"},
|
{"LPINTE", "Low Priority Interrupt Exception"},
|
{"LPINTE", "Low Priority Interrupt Exception"},
|
{"AE", "Alignment Exception"},
|
{"AE", "Alignment Exception"},
|
{"ILINSE" "Illegal Instruction" },
|
{"ILINSE" "Illegal Instruction" },
|
{"HPINTE", "High Priority Interrupt Exception"},
|
{"HPINTE", "High Priority Interrupt Exception"},
|
{"DME", "DTLB Miss Exception"},
|
{"DME", "DTLB Miss Exception"},
|
{"IME", "ITLB Miss Exception"},
|
{"IME", "ITLB Miss Exception"},
|
{"RE", "Range Exception"},
|
{"RE", "Range Exception"},
|
{"SCE", "SCE Exception"},
|
{"SCE", "SCE Exception"},
|
{"BRKPTE", "Breakpoint Exception"},
|
{"BRKPTE", "Breakpoint Exception"},
|
{NULL, NULL}
|
{NULL, NULL}
|
};
|
};
|
|
|
const char *compare_to_names[NUM_CT_NAMES] =
|
const char *compare_to_names[NUM_CT_NAMES] =
|
{
|
{
|
"DISABLED", "FETCH", "LEA", "SEA", "LDATA", "SDATA", "AEA", "ADATA"
|
"DISABLED", "FETCH", "LEA", "SEA", "LDATA", "SDATA", "AEA", "ADATA"
|
};
|
};
|
|
|
const char *or1k_record_names[MAX_RECORD_NAMES] =
|
const char *or1k_record_names[MAX_RECORD_NAMES] =
|
{
|
{
|
"PC", "LSEA", "LDATA", "SDATA", "READSPR", "WRITESPR", "INSTR"
|
"PC", "LSEA", "LDATA", "SDATA", "READSPR", "WRITESPR", "INSTR"
|
};
|
};
|
|
|
const char *or1k_is_names[MAX_IS_NAMES] =
|
const char *or1k_is_names[MAX_IS_NAMES] =
|
{
|
{
|
"IF_NONE", "IF_NORMAL", "IF_BRANCH", "IF_DELAY"
|
"IF_NONE", "IF_NORMAL", "IF_BRANCH", "IF_DELAY"
|
};
|
};
|
|
|
const char *or1k_ls_names[MAX_LS_NAMES] =
|
const char *or1k_ls_names[MAX_LS_NAMES] =
|
{
|
{
|
"LS_NONE", " ", "LBZ", "LBS", "LHZ", "LHS", "LWZ", "LWS",
|
"LS_NONE", " ", "LBZ", "LBS", "LHZ", "LHS", "LWZ", "LWS",
|
" ", " ", "SB", " ", "SH", " ", "SW", " "
|
" ", " ", "SB", " ", "SH", " ", "SW", " "
|
};
|
};
|
|
|
/* *INDENT-ON* */
|
/* *INDENT-ON* */
|
|
|
/* The list of available "set or1k " and "show or1k " commands */
|
/* The list of available "set or1k " and "show or1k " commands */
|
static struct cmd_list_element *htrace_cmdlist = NULL;
|
static struct cmd_list_element *htrace_cmdlist = NULL;
|
static struct cmd_list_element *htrace_mode_cmdlist = NULL;
|
static struct cmd_list_element *htrace_mode_cmdlist = NULL;
|
|
|
struct htrace_struct or1k_htrace;
|
struct htrace_struct or1k_htrace;
|
struct hwatch_struct or1k_hwatch[MAX_HW_WATCHES];
|
struct hwatch_struct or1k_hwatch[MAX_HW_WATCHES];
|
int num_hw_watches = 0;
|
int num_hw_watches = 0;
|
|
|
/* Trace data. */
|
/* Trace data. */
|
char trace_filename[TRACE_FILENAME_SIZE] = "trace.dat";
|
char trace_filename[TRACE_FILENAME_SIZE] = "trace.dat";
|
|
|
/* Size of trace file in records. */
|
/* Size of trace file in records. */
|
int trace_size = 0;
|
int trace_size = 0;
|
|
|
/* Previous values for buffer display. */
|
/* Previous values for buffer display. */
|
static int prev_length = 10;
|
static int prev_length = 10;
|
static int prev_from = 0;
|
static int prev_from = 0;
|
|
|
/* Converts regno to sprno. or1k debug unit has GPRs mapped to SPRs,
|
/* Converts regno to sprno. or1k debug unit has GPRs mapped to SPRs,
|
which are not compact, so we are mapping them for GDB. */
|
which are not compact, so we are mapping them for GDB. */
|
/* Rewritten by CZ 12/09/01 */
|
/* Rewritten by CZ 12/09/01 */
|
int
|
int
|
or1k_regnum_to_sprnum (int regno)
|
or1k_regnum_to_sprnum (int regno)
|
{
|
{
|
if(regno < MAX_GPR_REGS)
|
if(regno < MAX_GPR_REGS)
|
return SPR_REG(SPR_SYSTEM_GROUP, regno + CURRENT_CID * MAX_GPR_REGS + SPR_GPR_START);
|
return SPR_REG(SPR_SYSTEM_GROUP, regno + CURRENT_CID * MAX_GPR_REGS + SPR_GPR_START);
|
|
|
if (regno < MAX_GPR_REGS + MAX_VF_REGS)
|
if (regno < MAX_GPR_REGS + MAX_VF_REGS)
|
return SPR_REG(SPR_SYSTEM_GROUP, regno - MAX_GPR_REGS
|
return SPR_REG(SPR_SYSTEM_GROUP, regno - MAX_GPR_REGS
|
+ CURRENT_CID * MAX_GPR_REGS + SPR_VFPR_START);
|
+ CURRENT_CID * MAX_GPR_REGS + SPR_VFPR_START);
|
|
|
switch(regno)
|
switch(regno)
|
{
|
{
|
case PS_REGNUM: return SR_SPRNUM;
|
case PS_REGNUM: return SR_SPRNUM;
|
case PC_REGNUM: return PC_SPRNUM;
|
case PC_REGNUM: return PC_SPRNUM;
|
/*case CCR_REGNUM: return CCR_SPRNUM(CURRENT_CID);*/
|
/*case CCR_REGNUM: return CCR_SPRNUM(CURRENT_CID);*/
|
case EPCR_REGNUM: return EPCR_SPRNUM(CURRENT_CID);
|
case EPCR_REGNUM: return EPCR_SPRNUM(CURRENT_CID);
|
/*case EAR_REGNUM: return EAR_SPRNUM(CURRENT_CID);
|
/*case EAR_REGNUM: return EAR_SPRNUM(CURRENT_CID);
|
case ESR_REGNUM: return ESR_SPRNUM(CURRENT_CID);*/
|
case ESR_REGNUM: return ESR_SPRNUM(CURRENT_CID);*/
|
default:
|
default:
|
error("Invalid register number!");
|
error("Invalid register number!");
|
break;
|
break;
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Builds and returns register name. */
|
/* Builds and returns register name. */
|
|
|
static char tmp_name[16];
|
static char tmp_name[16];
|
static char *
|
static char *
|
or1k_spr_register_name (i)
|
or1k_spr_register_name (i)
|
int i;
|
int i;
|
{
|
{
|
int group = i >> SPR_GROUP_SIZE_BITS;
|
int group = i >> SPR_GROUP_SIZE_BITS;
|
int index = i & (SPR_GROUP_SIZE - 1);
|
int index = i & (SPR_GROUP_SIZE - 1);
|
switch (group)
|
switch (group)
|
{
|
{
|
/* Names covered in or1k_reg_names. */
|
/* Names covered in or1k_reg_names. */
|
case 0:
|
case 0:
|
|
|
/* Generate upper names. */
|
/* Generate upper names. */
|
if (index >= SPR_GPR_START)
|
if (index >= SPR_GPR_START)
|
{
|
{
|
if (index < SPR_VFPR_START)
|
if (index < SPR_VFPR_START)
|
sprintf (tmp_name, "GPR%i", index - SPR_GPR_START);
|
sprintf (tmp_name, "GPR%i", index - SPR_GPR_START);
|
else
|
else
|
sprintf (tmp_name, "VFR%i", index - SPR_VFPR_START);
|
sprintf (tmp_name, "VFR%i", index - SPR_VFPR_START);
|
return (char *)&tmp_name;
|
return (char *)&tmp_name;
|
}
|
}
|
case 3:
|
case 3:
|
case 4:
|
case 4:
|
case 5:
|
case 5:
|
case 6:
|
case 6:
|
case 7:
|
case 7:
|
case 8:
|
case 8:
|
case 9:
|
case 9:
|
case 10:
|
case 10:
|
{
|
{
|
int group_start = 0;
|
int group_start = 0;
|
for (i = 0; i < group; i++)
|
for (i = 0; i < group; i++)
|
group_start += or1k_group_name_sizes[i];
|
group_start += or1k_group_name_sizes[i];
|
|
|
if (index >= or1k_group_name_sizes[group])
|
if (index >= or1k_group_name_sizes[group])
|
{
|
{
|
sprintf (tmp_name, "SPR%i_%i", group, index);
|
sprintf (tmp_name, "SPR%i_%i", group, index);
|
return (char *)&tmp_name;
|
return (char *)&tmp_name;
|
}
|
}
|
else
|
else
|
return or1k_reg_names[group_start + index];
|
return or1k_reg_names[group_start + index];
|
}
|
}
|
|
|
/* Build names for DMMU group. */
|
/* Build names for DMMU group. */
|
case 1:
|
case 1:
|
case 2:
|
case 2:
|
strcpy (tmp_name, (group == 1)?"D":"I");
|
strcpy (tmp_name, (group == 1)?"D":"I");
|
switch (index)
|
switch (index)
|
{
|
{
|
case 16:
|
case 16:
|
return strcat (tmp_name, "MMUCR");
|
return strcat (tmp_name, "MMUCR");
|
case 17:
|
case 17:
|
return strcat (tmp_name, "MMUPR");
|
return strcat (tmp_name, "MMUPR");
|
case 18:
|
case 18:
|
return strcat (tmp_name, "TLBEIR");
|
return strcat (tmp_name, "TLBEIR");
|
case 24:
|
case 24:
|
return strcat (tmp_name, "ATBMR0");
|
return strcat (tmp_name, "ATBMR0");
|
case 25:
|
case 25:
|
return strcat (tmp_name, "ATBMR1");
|
return strcat (tmp_name, "ATBMR1");
|
case 26:
|
case 26:
|
return strcat (tmp_name, "ATBMR2");
|
return strcat (tmp_name, "ATBMR2");
|
case 27:
|
case 27:
|
return strcat (tmp_name, "ATBMR3");
|
return strcat (tmp_name, "ATBMR3");
|
case 28:
|
case 28:
|
return strcat (tmp_name, "ATBTR0");
|
return strcat (tmp_name, "ATBTR0");
|
case 29:
|
case 29:
|
return strcat (tmp_name, "ATBTR0");
|
return strcat (tmp_name, "ATBTR0");
|
case 30:
|
case 30:
|
return strcat (tmp_name, "ATBTR0");
|
return strcat (tmp_name, "ATBTR0");
|
case 31:
|
case 31:
|
return strcat (tmp_name, "ATBTR0");
|
return strcat (tmp_name, "ATBTR0");
|
default:
|
default:
|
if (index >= 1024) {
|
if (index >= 1024) {
|
index -= 1024;
|
index -= 1024;
|
sprintf (tmp_name, "%sTLBW%iMR%i", (group == 1)?"D":"I", index / 128, index % 128);
|
sprintf (tmp_name, "%sTLBW%iMR%i", (group == 1)?"D":"I", index / 128, index % 128);
|
return (char *)&tmp_name;
|
return (char *)&tmp_name;
|
}
|
}
|
sprintf (tmp_name, "SPR%i_%i", group, index);
|
sprintf (tmp_name, "SPR%i_%i", group, index);
|
return (char *)&tmp_name;
|
return (char *)&tmp_name;
|
}
|
}
|
default:
|
default:
|
sprintf (tmp_name, "SPR%i_%i", group, index);
|
sprintf (tmp_name, "SPR%i_%i", group, index);
|
return (char *)&tmp_name;
|
return (char *)&tmp_name;
|
}
|
}
|
}
|
}
|
|
|
/* Get register index in group from name. Negative if no match. */
|
/* Get register index in group from name. Negative if no match. */
|
|
|
static int
|
static int
|
or1k_regno_from_name (group, name)
|
or1k_regno_from_name (group, name)
|
int group;
|
int group;
|
char *name;
|
char *name;
|
{
|
{
|
int i;
|
int i;
|
if (toupper(name[0]) == 'S' && toupper(name[1]) == 'P' && toupper(name[2]) == 'R')
|
if (toupper(name[0]) == 'S' && toupper(name[1]) == 'P' && toupper(name[2]) == 'R')
|
{
|
{
|
char *ptr_c;
|
char *ptr_c;
|
name += 3;
|
name += 3;
|
i = (int) strtoul (name, &ptr_c, 10);
|
i = (int) strtoul (name, &ptr_c, 10);
|
if (*ptr_c != '_' || i != group)
|
if (*ptr_c != '_' || i != group)
|
return -1;
|
return -1;
|
ptr_c++;
|
ptr_c++;
|
i = (int) strtoul (name, &ptr_c, 10);
|
i = (int) strtoul (name, &ptr_c, 10);
|
if (*ptr_c)
|
if (*ptr_c)
|
return -1;
|
return -1;
|
else return i;
|
else return i;
|
}
|
}
|
for (i = 0; i < or1k_spr_valid_aliases[group]; i++)
|
for (i = 0; i < or1k_spr_valid_aliases[group]; i++)
|
{
|
{
|
char *s;
|
char *s;
|
s = or1k_spr_register_name (SPR_REG(group, i));
|
s = or1k_spr_register_name (SPR_REG(group, i));
|
if (strcasecmp (name, s) == 0)
|
if (strcasecmp (name, s) == 0)
|
return i;
|
return i;
|
}
|
}
|
return -1;
|
return -1;
|
}
|
}
|
|
|
/* Returns gdb register name. */
|
/* Returns gdb register name. */
|
|
|
char *
|
char *
|
or1k_register_name (regno)
|
or1k_register_name (regno)
|
int regno;
|
int regno;
|
{
|
{
|
return or1k_gdb_reg_names[regno];
|
return or1k_gdb_reg_names[regno];
|
}
|
}
|
|
|
/* Utility function to display vf regs. */
|
/* Utility function to display vf regs. */
|
|
|
static int
|
static int
|
do_vf_register (regnum)
|
do_vf_register (regnum)
|
int regnum;
|
int regnum;
|
{
|
{
|
/* do values for FP (float) regs */
|
/* do values for FP (float) regs */
|
char *raw_buffer;
|
char *raw_buffer;
|
|
|
/* doubles extracted from raw hex data */
|
/* doubles extracted from raw hex data */
|
double doub, flt;
|
double doub, flt;
|
int inv1, inv3, byte;
|
int inv1, inv3, byte;
|
|
|
raw_buffer = (char *) alloca (OR1K_VF_REGSIZE);
|
raw_buffer = (char *) alloca (OR1K_VF_REGSIZE);
|
|
|
/* Get the data in raw format. */
|
/* Get the data in raw format. */
|
if (read_relative_register_raw_bytes (regnum, raw_buffer))
|
if (read_relative_register_raw_bytes (regnum, raw_buffer))
|
error ("can't read register %d (%s)", regnum, REGISTER_NAME (regnum));
|
error ("can't read register %d (%s)", regnum, REGISTER_NAME (regnum));
|
|
|
flt = unpack_double (builtin_type_float, raw_buffer, &inv1);
|
flt = unpack_double (builtin_type_float, raw_buffer, &inv1);
|
doub = unpack_double (builtin_type_double, raw_buffer, &inv3);
|
doub = unpack_double (builtin_type_double, raw_buffer, &inv3);
|
|
|
if (inv1)
|
if (inv1)
|
printf_filtered (" %-5s flt: <invalid float>", REGISTER_NAME (regnum));
|
printf_filtered (" %-5s flt: <invalid float>", REGISTER_NAME (regnum));
|
else
|
else
|
printf_filtered (" %-5s flt:%-17.9g", REGISTER_NAME (regnum), flt);
|
printf_filtered (" %-5s flt:%-17.9g", REGISTER_NAME (regnum), flt);
|
printf_filtered (inv3 ? " dbl: <invalid double> " :
|
printf_filtered (inv3 ? " dbl: <invalid double> " :
|
" dbl: %-24.17g ", doub);
|
" dbl: %-24.17g ", doub);
|
|
|
/* pad small registers */
|
/* pad small registers */
|
for (byte = 0; byte < (OR1K_GPR_REGSIZE - REGISTER_VIRTUAL_SIZE (regnum)); byte++)
|
for (byte = 0; byte < (OR1K_GPR_REGSIZE - REGISTER_VIRTUAL_SIZE (regnum)); byte++)
|
printf_filtered (" ");
|
printf_filtered (" ");
|
|
|
/* Now print the register value in hex, endian order. */
|
/* Now print the register value in hex, endian order. */
|
if (TARGET_BYTE_ORDER == BIG_ENDIAN)
|
if (TARGET_BYTE_ORDER == BIG_ENDIAN)
|
for (byte = REGISTER_RAW_SIZE (regnum) - REGISTER_VIRTUAL_SIZE (regnum);
|
for (byte = REGISTER_RAW_SIZE (regnum) - REGISTER_VIRTUAL_SIZE (regnum);
|
byte < REGISTER_RAW_SIZE (regnum);
|
byte < REGISTER_RAW_SIZE (regnum);
|
byte++)
|
byte++)
|
printf_filtered ("%02x", (unsigned char) raw_buffer[byte]);
|
printf_filtered ("%02x", (unsigned char) raw_buffer[byte]);
|
else
|
else
|
for (byte = REGISTER_VIRTUAL_SIZE (regnum) - 1;
|
for (byte = REGISTER_VIRTUAL_SIZE (regnum) - 1;
|
byte >= 0;
|
byte >= 0;
|
byte--)
|
byte--)
|
printf_filtered ("%02x", (unsigned char) raw_buffer[byte]);
|
printf_filtered ("%02x", (unsigned char) raw_buffer[byte]);
|
printf_filtered ("\n");
|
printf_filtered ("\n");
|
|
|
regnum++;
|
regnum++;
|
return regnum;
|
return regnum;
|
}
|
}
|
|
|
/* Print a row's worth of GP (int) registers, with name labels above */
|
/* Print a row's worth of GP (int) registers, with name labels above */
|
|
|
static int
|
static int
|
do_gp_register_row (regnum)
|
do_gp_register_row (regnum)
|
int regnum;
|
int regnum;
|
{
|
{
|
/* do values for GP (int) regs */
|
/* do values for GP (int) regs */
|
char raw_buffer[MAX_REGISTER_RAW_SIZE];
|
char raw_buffer[MAX_REGISTER_RAW_SIZE];
|
|
|
/* display cols per row */
|
/* display cols per row */
|
int ncols = (OR1K_64BIT_IMPLEMENTATION ? 4 : 8);
|
int ncols = (OR1K_64BIT_IMPLEMENTATION ? 4 : 8);
|
int col, byte;
|
int col, byte;
|
int start_regnum = regnum;
|
int start_regnum = regnum;
|
int numregs = NUM_REGS;
|
int numregs = NUM_REGS;
|
|
|
/* For GP registers, we print a separate row of names above the vals */
|
/* For GP registers, we print a separate row of names above the vals */
|
printf_filtered (" ");
|
printf_filtered (" ");
|
for (col = 0; col < ncols && regnum < numregs; regnum++)
|
for (col = 0; col < ncols && regnum < numregs; regnum++)
|
{
|
{
|
if (*REGISTER_NAME (regnum) == '\0')
|
if (*REGISTER_NAME (regnum) == '\0')
|
continue; /* unused register */
|
continue; /* unused register */
|
if (OR1K_IS_VF(regnum))
|
if (OR1K_IS_VF(regnum))
|
break; /* end the row: reached VF register */
|
break; /* end the row: reached VF register */
|
printf_filtered (OR1K_64BIT_IMPLEMENTATION ? "%17s" : "%9s",
|
printf_filtered (OR1K_64BIT_IMPLEMENTATION ? "%17s" : "%9s",
|
REGISTER_NAME (regnum));
|
REGISTER_NAME (regnum));
|
col++;
|
col++;
|
}
|
}
|
printf_filtered ("\n ");
|
printf_filtered ("\n ");
|
|
|
regnum = start_regnum; /* go back to start of row */
|
regnum = start_regnum; /* go back to start of row */
|
|
|
/* now print the values in hex, 4 or 8 to the row */
|
/* now print the values in hex, 4 or 8 to the row */
|
for (col = 0; col < ncols && regnum < numregs; regnum++)
|
for (col = 0; col < ncols && regnum < numregs; regnum++)
|
{
|
{
|
/* unused register? */
|
/* unused register? */
|
if (*REGISTER_NAME (regnum) == '\0')
|
if (*REGISTER_NAME (regnum) == '\0')
|
continue;
|
continue;
|
|
|
/* end row: reached VF register? */
|
/* end row: reached VF register? */
|
if (OR1K_IS_VF(regnum))
|
if (OR1K_IS_VF(regnum))
|
break;
|
break;
|
|
|
/* OK: get the data in raw format. */
|
/* OK: get the data in raw format. */
|
if (read_relative_register_raw_bytes (regnum, raw_buffer))
|
if (read_relative_register_raw_bytes (regnum, raw_buffer))
|
error ("can't read register %d (%s)", regnum, REGISTER_NAME (regnum));
|
error ("can't read register %d (%s)", regnum, REGISTER_NAME (regnum));
|
|
|
/* pad small registers */
|
/* pad small registers */
|
for (byte = 0; byte < (OR1K_GPR_REGSIZE - REGISTER_VIRTUAL_SIZE (regnum)); byte++)
|
for (byte = 0; byte < (OR1K_GPR_REGSIZE - REGISTER_VIRTUAL_SIZE (regnum)); byte++)
|
printf_filtered (" ");
|
printf_filtered (" ");
|
|
|
/* Now print the register value in hex, endian order. */
|
/* Now print the register value in hex, endian order. */
|
if (TARGET_BYTE_ORDER == BIG_ENDIAN)
|
if (TARGET_BYTE_ORDER == BIG_ENDIAN)
|
for (byte = REGISTER_RAW_SIZE (regnum) - REGISTER_VIRTUAL_SIZE (regnum);
|
for (byte = REGISTER_RAW_SIZE (regnum) - REGISTER_VIRTUAL_SIZE (regnum);
|
byte < REGISTER_RAW_SIZE (regnum);
|
byte < REGISTER_RAW_SIZE (regnum);
|
byte++)
|
byte++)
|
printf_filtered ("%02x", (unsigned char) raw_buffer[byte]);
|
printf_filtered ("%02x", (unsigned char) raw_buffer[byte]);
|
else
|
else
|
for (byte = REGISTER_VIRTUAL_SIZE (regnum) - 1;
|
for (byte = REGISTER_VIRTUAL_SIZE (regnum) - 1;
|
byte >= 0;
|
byte >= 0;
|
byte--)
|
byte--)
|
printf_filtered ("%02x", (unsigned char) raw_buffer[byte]);
|
printf_filtered ("%02x", (unsigned char) raw_buffer[byte]);
|
printf_filtered (" ");
|
printf_filtered (" ");
|
col++;
|
col++;
|
}
|
}
|
|
|
/* ie. if we actually printed anything... */
|
/* ie. if we actually printed anything... */
|
if (col > 0)
|
if (col > 0)
|
printf_filtered ("\n");
|
printf_filtered ("\n");
|
|
|
return regnum;
|
return regnum;
|
}
|
}
|
|
|
/* Replacement for generic do_registers_info.
|
/* Replacement for generic do_registers_info.
|
Print regs in pretty columns. */
|
Print regs in pretty columns. */
|
|
|
static void
|
static void
|
print_register (regnum, all)
|
print_register (regnum, all)
|
int regnum, all;
|
int regnum, all;
|
{
|
{
|
int offset;
|
int offset;
|
char raw_buffer[MAX_REGISTER_RAW_SIZE];
|
char raw_buffer[MAX_REGISTER_RAW_SIZE];
|
|
|
/* Get the data in raw format. */
|
/* Get the data in raw format. */
|
if (read_relative_register_raw_bytes (regnum, raw_buffer))
|
if (read_relative_register_raw_bytes (regnum, raw_buffer))
|
{
|
{
|
printf_filtered ("%s: [Invalid]", REGISTER_NAME (regnum));
|
printf_filtered ("%s: [Invalid]", REGISTER_NAME (regnum));
|
return;
|
return;
|
}
|
}
|
|
|
/* If virtual format is floating, print it that way. */
|
/* If virtual format is floating, print it that way. */
|
if (OR1K_IS_VF (regnum))
|
if (OR1K_IS_VF (regnum))
|
do_vf_register (regnum);
|
do_vf_register (regnum);
|
else
|
else
|
{
|
{
|
int byte;
|
int byte;
|
printf_filtered ("%-16s\t", REGISTER_NAME (regnum));
|
printf_filtered ("%-16s\t", REGISTER_NAME (regnum));
|
|
|
/* pad small registers */
|
/* pad small registers */
|
for (byte = 0; byte < (OR1K_GPR_REGSIZE - REGISTER_VIRTUAL_SIZE (regnum)); byte++)
|
for (byte = 0; byte < (OR1K_GPR_REGSIZE - REGISTER_VIRTUAL_SIZE (regnum)); byte++)
|
printf_filtered (" ");
|
printf_filtered (" ");
|
|
|
/* Now print the register value in hex, endian order. */
|
/* Now print the register value in hex, endian order. */
|
if (TARGET_BYTE_ORDER == BIG_ENDIAN)
|
if (TARGET_BYTE_ORDER == BIG_ENDIAN)
|
for (byte = REGISTER_RAW_SIZE (regnum) - REGISTER_VIRTUAL_SIZE (regnum);
|
for (byte = REGISTER_RAW_SIZE (regnum) - REGISTER_VIRTUAL_SIZE (regnum);
|
byte < REGISTER_RAW_SIZE (regnum);
|
byte < REGISTER_RAW_SIZE (regnum);
|
byte++)
|
byte++)
|
printf_filtered ("%02x", (unsigned char) raw_buffer[byte]);
|
printf_filtered ("%02x", (unsigned char) raw_buffer[byte]);
|
else
|
else
|
for (byte = REGISTER_VIRTUAL_SIZE (regnum) - 1;
|
for (byte = REGISTER_VIRTUAL_SIZE (regnum) - 1;
|
byte >= 0;
|
byte >= 0;
|
byte--)
|
byte--)
|
printf_filtered ("%02x", (unsigned char) raw_buffer[byte]);
|
printf_filtered ("%02x", (unsigned char) raw_buffer[byte]);
|
printf_filtered (" ");
|
printf_filtered (" ");
|
}
|
}
|
}
|
}
|
|
|
/* DO_REGISTERS_INFO: called by "info register" command */
|
/* DO_REGISTERS_INFO: called by "info register" command */
|
|
|
void
|
void
|
or1k_do_registers_info (regnum, fpregs)
|
or1k_do_registers_info (regnum, fpregs)
|
int regnum;
|
int regnum;
|
int fpregs;
|
int fpregs;
|
{
|
{
|
if (fpregs && !or1k_implementation.vf_present)
|
if (fpregs && !or1k_implementation.vf_present)
|
{
|
{
|
warning ("VF register set not present in this implementation.");
|
warning ("VF register set not present in this implementation.");
|
fpregs = 0;
|
fpregs = 0;
|
}
|
}
|
if (regnum != -1)
|
if (regnum != -1)
|
{
|
{
|
/* do one specified register */
|
/* do one specified register */
|
if (*(REGISTER_NAME (regnum)) == '\0')
|
if (*(REGISTER_NAME (regnum)) == '\0')
|
error ("Not a valid register for the current processor type");
|
error ("Not a valid register for the current processor type");
|
|
|
print_register (regnum, 0);
|
print_register (regnum, 0);
|
printf_filtered ("\n");
|
printf_filtered ("\n");
|
}
|
}
|
else
|
else
|
/* do all (or most) registers */
|
/* do all (or most) registers */
|
{
|
{
|
regnum = 0;
|
regnum = 0;
|
while (regnum < NUM_REGS)
|
while (regnum < NUM_REGS)
|
{
|
{
|
if (OR1K_IS_VF (regnum))
|
if (OR1K_IS_VF (regnum))
|
/* true for "INFO ALL-REGISTERS" command */
|
/* true for "INFO ALL-REGISTERS" command */
|
if (fpregs)
|
if (fpregs)
|
/* FP regs */
|
/* FP regs */
|
regnum = do_vf_register (regnum);
|
regnum = do_vf_register (regnum);
|
else
|
else
|
/* skip floating point regs */
|
/* skip floating point regs */
|
regnum++;
|
regnum++;
|
else
|
else
|
/* GP (int) regs */
|
/* GP (int) regs */
|
regnum = do_gp_register_row (regnum);
|
regnum = do_gp_register_row (regnum);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Given the address at which to insert a breakpoint (BP_ADDR),
|
/* Given the address at which to insert a breakpoint (BP_ADDR),
|
what will that breakpoint be?
|
what will that breakpoint be?
|
|
|
For or1k, we have a breakpoint instruction. Since all or1k
|
For or1k, we have a breakpoint instruction. Since all or1k
|
instructions are 32 bits, this is all we need, regardless of
|
instructions are 32 bits, this is all we need, regardless of
|
address. K is not used. */
|
address. K is not used. */
|
|
|
unsigned char *
|
unsigned char *
|
or1k_breakpoint_from_pc (bp_addr, bp_size)
|
or1k_breakpoint_from_pc (bp_addr, bp_size)
|
CORE_ADDR * bp_addr;
|
CORE_ADDR * bp_addr;
|
int *bp_size;
|
int *bp_size;
|
{
|
{
|
static char breakpoint[] = BRK_INSTR_STRUCT;
|
static char breakpoint[] = BRK_INSTR_STRUCT;
|
*bp_size = OR1K_INSTLEN;
|
*bp_size = OR1K_INSTLEN;
|
return breakpoint;
|
return breakpoint;
|
}
|
}
|
|
|
/* Return the string for a signal.
|
/* Return the string for a signal.
|
Replacement for target_signal_to_string (sig). NOT USED. */
|
Replacement for target_signal_to_string (sig). NOT USED. */
|
|
|
char
|
char
|
*or1k_signal_to_string (sig)
|
*or1k_signal_to_string (sig)
|
enum target_signal sig;
|
enum target_signal sig;
|
{
|
{
|
if ((sig >= TARGET_SIGNAL_FIRST) && (sig <= TARGET_SIGNAL_LAST))
|
if ((sig >= TARGET_SIGNAL_FIRST) && (sig <= TARGET_SIGNAL_LAST))
|
return or1k_signals[sig].string;
|
return or1k_signals[sig].string;
|
else
|
else
|
if (sig <= TARGET_SIGNAL_LAST + NUM_OR1K_SIGNALS)
|
if (sig <= TARGET_SIGNAL_LAST + NUM_OR1K_SIGNALS)
|
return or1k_signals[sig - TARGET_SIGNAL_LAST].string;
|
return or1k_signals[sig - TARGET_SIGNAL_LAST].string;
|
else
|
else
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Return the name for a signal. */
|
/* Return the name for a signal. */
|
|
|
char *
|
char *
|
or1k_signal_to_name (sig)
|
or1k_signal_to_name (sig)
|
enum target_signal sig;
|
enum target_signal sig;
|
{
|
{
|
if (sig >= TARGET_SIGNAL_LAST)
|
if (sig >= TARGET_SIGNAL_LAST)
|
if (sig <= TARGET_SIGNAL_LAST + NUM_OR1K_SIGNALS)
|
if (sig <= TARGET_SIGNAL_LAST + NUM_OR1K_SIGNALS)
|
return or1k_signals[sig - TARGET_SIGNAL_LAST].name;
|
return or1k_signals[sig - TARGET_SIGNAL_LAST].name;
|
else
|
else
|
|
|
/* I think the code which prints this will always print it along with
|
/* I think the code which prints this will always print it along with
|
the string, so no need to be verbose. */
|
the string, so no need to be verbose. */
|
return "?";
|
return "?";
|
if (sig == TARGET_SIGNAL_UNKNOWN)
|
if (sig == TARGET_SIGNAL_UNKNOWN)
|
return "?";
|
return "?";
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Given a name, return its signal. NOT USED. */
|
/* Given a name, return its signal. NOT USED. */
|
enum target_signal
|
enum target_signal
|
or1k_signal_from_name (name)
|
or1k_signal_from_name (name)
|
char *name;
|
char *name;
|
{
|
{
|
enum target_signal sig;
|
enum target_signal sig;
|
|
|
/* It's possible we also should allow "SIGCLD" as well as "SIGCHLD"
|
/* It's possible we also should allow "SIGCLD" as well as "SIGCHLD"
|
for TARGET_SIGNAL_SIGCHLD. SIGIOT, on the other hand, is more
|
for TARGET_SIGNAL_SIGCHLD. SIGIOT, on the other hand, is more
|
questionable; seems like by now people should call it SIGABRT
|
questionable; seems like by now people should call it SIGABRT
|
instead. */
|
instead. */
|
|
|
/* This ugly cast brought to you by the native VAX compiler. */
|
/* This ugly cast brought to you by the native VAX compiler. */
|
for (sig = TARGET_SIGNAL_FIRST;
|
for (sig = TARGET_SIGNAL_FIRST;
|
or1k_signal_to_name (sig) != NULL;
|
or1k_signal_to_name (sig) != NULL;
|
sig = (enum target_signal) ((int) sig + 1))
|
sig = (enum target_signal) ((int) sig + 1))
|
if (STREQ (name, or1k_signal_to_name (sig)))
|
if (STREQ (name, or1k_signal_to_name (sig)))
|
return sig;
|
return sig;
|
return TARGET_SIGNAL_UNKNOWN;
|
return TARGET_SIGNAL_UNKNOWN;
|
}
|
}
|
|
|
/* Given a return value in `regbuf' with a type `valtype', extract and
|
/* Given a return value in `regbuf' with a type `valtype', extract and
|
copy its value into `valbuf'. */
|
copy its value into `valbuf'. */
|
|
|
void
|
void
|
or1k_extract_return_value (valtype, regbuf, valbuf)
|
or1k_extract_return_value (valtype, regbuf, valbuf)
|
struct type *valtype;
|
struct type *valtype;
|
char regbuf[REGISTER_BYTES];
|
char regbuf[REGISTER_BYTES];
|
char *valbuf;
|
char *valbuf;
|
{
|
{
|
if (TYPE_CODE_FLT == TYPE_CODE (valtype))
|
if (TYPE_CODE_FLT == TYPE_CODE (valtype))
|
memcpy (valbuf, ®buf[REGISTER_BYTE (VFRV_REGNUM)], TYPE_LENGTH (valtype));
|
memcpy (valbuf, ®buf[REGISTER_BYTE (VFRV_REGNUM)], TYPE_LENGTH (valtype));
|
else
|
else
|
memcpy (valbuf, ®buf[REGISTER_BYTE (RV_REGNUM)], TYPE_LENGTH (valtype));
|
memcpy (valbuf, ®buf[REGISTER_BYTE (RV_REGNUM)], TYPE_LENGTH (valtype));
|
}
|
}
|
|
|
/* Given a register index, return the first byte of the register within a remote
|
/* Given a register index, return the first byte of the register within a remote
|
protocol packet. This applies only to serial targets, not jtag or sim. */
|
protocol packet. This applies only to serial targets, not jtag or sim. */
|
int
|
int
|
or1k_register_byte(regnum)
|
or1k_register_byte(regnum)
|
int regnum;
|
int regnum;
|
{
|
{
|
int byte_offset;
|
int byte_offset;
|
|
|
byte_offset = 0;
|
byte_offset = 0;
|
if (regnum < MAX_GPR_REGS) {
|
if (regnum < MAX_GPR_REGS) {
|
if (regnum < NUM_GPR_REGS)
|
if (regnum < NUM_GPR_REGS)
|
return byte_offset + regnum * OR1K_GPR_REGSIZE;
|
return byte_offset + regnum * OR1K_GPR_REGSIZE;
|
else
|
else
|
return -1;
|
return -1;
|
}
|
}
|
|
|
byte_offset += NUM_GPR_REGS * OR1K_GPR_REGSIZE;
|
byte_offset += NUM_GPR_REGS * OR1K_GPR_REGSIZE;
|
regnum -= MAX_GPR_REGS;
|
regnum -= MAX_GPR_REGS;
|
if (regnum < MAX_VF_REGS) {
|
if (regnum < MAX_VF_REGS) {
|
if (regnum < NUM_VF_REGS)
|
if (regnum < NUM_VF_REGS)
|
return byte_offset + regnum * OR1K_VF_REGSIZE;
|
return byte_offset + regnum * OR1K_VF_REGSIZE;
|
else
|
else
|
return -1;
|
return -1;
|
}
|
}
|
|
|
/* Must be PC, SR, or EPC */
|
/* Must be PC, SR, or EPC */
|
byte_offset += NUM_VF_REGS * OR1K_VF_REGSIZE;
|
byte_offset += NUM_VF_REGS * OR1K_VF_REGSIZE;
|
regnum -= MAX_VF_REGS;
|
regnum -= MAX_VF_REGS;
|
if (regnum < 3)
|
if (regnum < 3)
|
return byte_offset + regnum * OR1K_SPR_REGSIZE;
|
return byte_offset + regnum * OR1K_SPR_REGSIZE;
|
|
|
/* Illegal register */
|
/* Illegal register */
|
return -1;
|
return -1;
|
}
|
}
|
|
|
/* Number of bytes of storage in the actual machine representation for
|
/* Number of bytes of storage in the actual machine representation for
|
register N. NOTE: This indirectly defines the register size
|
register N. NOTE: This indirectly defines the register size
|
transfered by the GDB protocol. If we have 64bit processor
|
transfered by the GDB protocol. If we have 64bit processor
|
implementation, GPR register raw size is 8B, otherwise 4B. */
|
implementation, GPR register raw size is 8B, otherwise 4B. */
|
int
|
int
|
or1k_register_raw_size(int regnum)
|
or1k_register_raw_size(int regnum)
|
{
|
{
|
int byte_offset;
|
int byte_offset;
|
|
|
if (regnum < MAX_GPR_REGS) {
|
if (regnum < MAX_GPR_REGS) {
|
if (regnum < NUM_GPR_REGS)
|
if (regnum < NUM_GPR_REGS)
|
return OR1K_GPR_REGSIZE;
|
return OR1K_GPR_REGSIZE;
|
else
|
else
|
return 0;
|
return 0;
|
}
|
}
|
|
|
regnum -= MAX_GPR_REGS;
|
regnum -= MAX_GPR_REGS;
|
if (regnum < MAX_VF_REGS) {
|
if (regnum < MAX_VF_REGS) {
|
if (regnum < NUM_VF_REGS)
|
if (regnum < NUM_VF_REGS)
|
return OR1K_VF_REGSIZE;
|
return OR1K_VF_REGSIZE;
|
else
|
else
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Must be PC, SR, or EPC */
|
/* Must be PC, SR, or EPC */
|
regnum -= MAX_VF_REGS;
|
regnum -= MAX_VF_REGS;
|
if (regnum < 3)
|
if (regnum < 3)
|
return OR1K_SPR_REGSIZE;
|
return OR1K_SPR_REGSIZE;
|
|
|
/* Illegal register */
|
/* Illegal register */
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Extra arch-specific data tied to frame */
|
/* Extra arch-specific data tied to frame */
|
struct frame_extra_info
|
struct frame_extra_info
|
{
|
{
|
/* Size of stack frame, in bytes. Produced by skip_prologue() */
|
/* Size of stack frame, in bytes. Produced by skip_prologue() */
|
int frame_size;
|
int frame_size;
|
|
|
/* List of frame offsets of all registers saved in a function's
|
/* List of frame offsets of all registers saved in a function's
|
prologue. This is deduced by skip_prologue(). Offset is in bytes,
|
prologue. This is deduced by skip_prologue(). Offset is in bytes,
|
relative to sp. If value is -1, corresponding reg is not saved by
|
relative to sp. If value is -1, corresponding reg is not saved by
|
the prologue code. */
|
the prologue code. */
|
int saved_reg_frame_offset[NUM_REGS];
|
int saved_reg_frame_offset[NUM_REGS];
|
|
|
/* Address of first instruction after the last prologue instruction;
|
/* Address of first instruction after the last prologue instruction;
|
Note that there may be instructions from the function's body
|
Note that there may be instructions from the function's body
|
intermingled with the prologue.
|
intermingled with the prologue.
|
If value is -1, not initialized. */
|
If value is -1, not initialized. */
|
CORE_ADDR after_prologue;
|
CORE_ADDR after_prologue;
|
};
|
};
|
|
|
/* Initialize arch-specific frame data */
|
/* Initialize arch-specific frame data */
|
void
|
void
|
or1k_init_extra_frame_info (int fromleaf, struct frame_info *frame)
|
or1k_init_extra_frame_info (int fromleaf, struct frame_info *frame)
|
{
|
{
|
int i;
|
int i;
|
|
|
frame->extra_info = (struct frame_extra_info *)
|
frame->extra_info = (struct frame_extra_info *)
|
frame_obstack_alloc (sizeof (struct frame_extra_info));
|
frame_obstack_alloc (sizeof (struct frame_extra_info));
|
|
|
frame->extra_info->after_prologue = -1;
|
frame->extra_info->after_prologue = -1;
|
frame->extra_info->frame_size = -1;
|
frame->extra_info->frame_size = -1;
|
for (i = 0; i < NUM_REGS; i++)
|
for (i = 0; i < NUM_REGS; i++)
|
frame->extra_info->saved_reg_frame_offset[i] = -1;
|
frame->extra_info->saved_reg_frame_offset[i] = -1;
|
}
|
}
|
|
|
/* For stack frame sizes less than 0x8000, the or1k version of gcc
|
/* For stack frame sizes less than 0x8000, the or1k version of gcc
|
emits the following prologue:
|
emits the following prologue:
|
|
|
l.addi r1, r1, -<STACK_FRAME_SIZE>
|
l.addi r1, r1, -<STACK_FRAME_SIZE>
|
l.sw <FP_OFFSET>(r1),r2
|
l.sw <FP_OFFSET>(r1),r2
|
l.addi r2, r1, <STACK_FRAME_SIZE>
|
l.addi r2, r1, <STACK_FRAME_SIZE>
|
|
|
; Save regs that will be clobbered
|
; Save regs that will be clobbered
|
l.sw <OFFSET_Rx>(r1),rx
|
l.sw <OFFSET_Rx>(r1),rx
|
l.sw <OFFSET_Ry>(r1),ry
|
l.sw <OFFSET_Ry>(r1),ry
|
; ...
|
; ...
|
|
|
*/
|
*/
|
|
|
/* FIXME: Does not work with frames greater than 0x7fff in size */
|
/* FIXME: Does not work with frames greater than 0x7fff in size */
|
|
|
/* If pframeless is non-NULL, only parse enough of the prologue to
|
/* If pframeless is non-NULL, only parse enough of the prologue to
|
determine if a function has a frame or not and return the result in
|
determine if a function has a frame or not and return the result in
|
*pframeless. This may save some remote transactions with the
|
*pframeless. This may save some remote transactions with the
|
target. */
|
target. */
|
CORE_ADDR
|
CORE_ADDR
|
or1k_skip_prologue (CORE_ADDR pc, struct frame_info *fi)
|
or1k_skip_prologue (CORE_ADDR pc, struct frame_info *fi)
|
{
|
{
|
unsigned long insn;
|
unsigned long insn;
|
CORE_ADDR skip_pc;
|
CORE_ADDR skip_pc;
|
CORE_ADDR func_addr, func_end;
|
CORE_ADDR func_addr, func_end;
|
struct symtab_and_line sal;
|
struct symtab_and_line sal;
|
int i, frameless;
|
int i, frameless;
|
int offset = 0;
|
int offset = 0;
|
int rB, immediate, immediate_hi;
|
int rB, immediate, immediate_hi;
|
int* saved_reg_frame_offset;
|
int* saved_reg_frame_offset;
|
|
|
/* Check to see if prologue analysis is cached from prior call */
|
/* Check to see if prologue analysis is cached from prior call */
|
if (fi && fi->extra_info->after_prologue != -1)
|
if (fi && fi->extra_info->after_prologue != -1)
|
return fi->extra_info->after_prologue;
|
return fi->extra_info->after_prologue;
|
|
|
if (fi)
|
if (fi)
|
saved_reg_frame_offset = &fi->extra_info->saved_reg_frame_offset[0];
|
saved_reg_frame_offset = &fi->extra_info->saved_reg_frame_offset[0];
|
|
|
/* Is there a prologue? */
|
/* Is there a prologue? */
|
insn = or1k_fetch_instruction (pc);
|
insn = or1k_fetch_instruction (pc);
|
if ((insn & 0xFFFF0000) != 0x9c210000) /* l.addi r1,r1,I */
|
if ((insn & 0xFFFF0000) != 0x9c210000) /* l.addi r1,r1,I */
|
goto done;
|
goto done;
|
|
|
pc += OR1K_INSTLEN;
|
pc += OR1K_INSTLEN;
|
|
|
insn = or1k_fetch_instruction (pc);
|
insn = or1k_fetch_instruction (pc);
|
if ((insn & 0xfc1ff800) != 0xd4011000) /* l.sw I(r1),r2 */
|
if ((insn & 0xfc1ff800) != 0xd4011000) /* l.sw I(r1),r2 */
|
goto done;
|
goto done;
|
immediate_hi = (insn & 0x03E00000) >> 10;
|
immediate_hi = (insn & 0x03E00000) >> 10;
|
immediate = (insn & 0x000007FF) | immediate_hi;
|
immediate = (insn & 0x000007FF) | immediate_hi;
|
pc += OR1K_INSTLEN;
|
pc += OR1K_INSTLEN;
|
if (fi)
|
if (fi)
|
saved_reg_frame_offset[2] = immediate;
|
saved_reg_frame_offset[2] = immediate;
|
|
|
insn = or1k_fetch_instruction (pc);
|
insn = or1k_fetch_instruction (pc);
|
if ((insn & 0xFFFF0000) != 0x9c410000) /* l.addi r2,r1,I */
|
if ((insn & 0xFFFF0000) != 0x9c410000) /* l.addi r2,r1,I */
|
goto done;
|
goto done;
|
if (fi)
|
if (fi)
|
fi->extra_info->frame_size = insn & 0xFFFF;
|
fi->extra_info->frame_size = insn & 0xFFFF;
|
pc += OR1K_INSTLEN;
|
pc += OR1K_INSTLEN;
|
|
|
/* Skip stored registers. */
|
/* Skip stored registers. */
|
insn = or1k_fetch_instruction (pc);
|
insn = or1k_fetch_instruction (pc);
|
while ((insn & 0xfc1f0000) == 0xd4010000) /* l.sw I(r1),rx */
|
while ((insn & 0xfc1f0000) == 0xd4010000) /* l.sw I(r1),rx */
|
{
|
{
|
rB = (insn & 0x0000F800) >> 11;
|
rB = (insn & 0x0000F800) >> 11;
|
immediate_hi = (insn & 0x03E00000) >> 10;
|
immediate_hi = (insn & 0x03E00000) >> 10;
|
immediate = (insn & 0x000007FF) | immediate_hi;
|
immediate = (insn & 0x000007FF) | immediate_hi;
|
|
|
/* get saved reg. */
|
/* get saved reg. */
|
if (fi)
|
if (fi)
|
saved_reg_frame_offset[rB] = immediate;
|
saved_reg_frame_offset[rB] = immediate;
|
pc += OR1K_INSTLEN;
|
pc += OR1K_INSTLEN;
|
insn = or1k_fetch_instruction (pc);
|
insn = or1k_fetch_instruction (pc);
|
}
|
}
|
done:
|
done:
|
if (fi)
|
if (fi)
|
fi->extra_info->after_prologue = pc;
|
fi->extra_info->after_prologue = pc;
|
return pc;
|
return pc;
|
}
|
}
|
|
|
/* Determines whether this function has frame. */
|
/* Determines whether this function has frame. */
|
|
|
int
|
int
|
or1k_frameless_function_invocation (struct frame_info *fi)
|
or1k_frameless_function_invocation (struct frame_info *fi)
|
{
|
{
|
int frameless;
|
int frameless;
|
CORE_ADDR func_start, after_prologue;
|
CORE_ADDR func_start, after_prologue;
|
func_start = (get_pc_function_start ((fi)->pc) + FUNCTION_START_OFFSET);
|
func_start = (get_pc_function_start ((fi)->pc) + FUNCTION_START_OFFSET);
|
|
|
/* If we don't skip pc, we don't have even shortest possible prologue. */
|
/* If we don't skip pc, we don't have even shortest possible prologue. */
|
after_prologue = or1k_skip_prologue (func_start, fi);
|
after_prologue = or1k_skip_prologue (func_start, fi);
|
frameless = (after_prologue <= func_start);
|
frameless = (after_prologue <= func_start);
|
return frameless;
|
return frameless;
|
}
|
}
|
|
|
/* Given a GDB frame, determine the address of the calling function's frame.
|
/* Given a GDB frame, determine the address of the calling function's frame.
|
This will be used to create a new GDB frame struct, and then
|
This will be used to create a new GDB frame struct, and then
|
INIT_EXTRA_FRAME_INFO and INIT_FRAME_PC will be called for the new frame. */
|
INIT_EXTRA_FRAME_INFO and INIT_FRAME_PC will be called for the new frame. */
|
|
|
CORE_ADDR
|
CORE_ADDR
|
or1k_frame_chain (frame)
|
or1k_frame_chain (frame)
|
struct frame_info *frame;
|
struct frame_info *frame;
|
{
|
{
|
CORE_ADDR fp = 0;
|
CORE_ADDR fp = 0;
|
if (USE_GENERIC_DUMMY_FRAMES)
|
if (USE_GENERIC_DUMMY_FRAMES)
|
{
|
{
|
if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame))
|
if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame))
|
/* dummy frame same as caller's frame */
|
/* dummy frame same as caller's frame */
|
return frame->frame;
|
return frame->frame;
|
}
|
}
|
|
|
if (inside_entry_file (frame->pc) ||
|
if (inside_entry_file (frame->pc) ||
|
frame->pc == entry_point_address ())
|
frame->pc == entry_point_address ())
|
return 0;
|
return 0;
|
|
|
if (frame->signal_handler_caller)
|
if (frame->signal_handler_caller)
|
fp = read_memory_integer (frame->frame, 4);
|
fp = read_memory_integer (frame->frame, 4);
|
else if (frame->next != NULL
|
else if (frame->next != NULL
|
&& frame->next->signal_handler_caller
|
&& frame->next->signal_handler_caller
|
&& FRAMELESS_FUNCTION_INVOCATION (frame))
|
&& FRAMELESS_FUNCTION_INVOCATION (frame))
|
/* A frameless function interrupted by a signal did not change the
|
/* A frameless function interrupted by a signal did not change the
|
frame pointer. */
|
frame pointer. */
|
fp = FRAME_FP (frame);
|
fp = FRAME_FP (frame);
|
else
|
else
|
{
|
{
|
unsigned long func_pc = get_pc_function_start(frame->pc);
|
unsigned long func_pc = get_pc_function_start(frame->pc);
|
unsigned long insn = read_memory_integer(func_pc,4);
|
unsigned long insn = read_memory_integer(func_pc,4);
|
int i;
|
int i;
|
int offset = 0;
|
int offset = 0;
|
|
|
/* The first instruction should tell us the number of bytes
|
/* The first instruction should tell us the number of bytes
|
in our frame. If it isn't we're in trouble because
|
in our frame. If it isn't we're in trouble because
|
the function is without a prologue... */
|
the function is without a prologue... */
|
if ((insn & 0xFFFF0000) == 0x9c210000) /* l.addi r1,r1,I */
|
if ((insn & 0xFFFF0000) == 0x9c210000) /* l.addi r1,r1,I */
|
{
|
{
|
/* Sign extend immediate */
|
/* Sign extend immediate */
|
int immediate = (long)(insn << 16) >> 16;
|
int immediate = (long)(insn << 16) >> 16;
|
int frame_size = -immediate;
|
int frame_size = -immediate;
|
|
|
/* Look for the storage of the frame pointer in the
|
/* Look for the storage of the frame pointer in the
|
function prologue.. */
|
function prologue.. */
|
unsigned long insn = read_memory_integer(func_pc+4,4);
|
unsigned long insn = read_memory_integer(func_pc+4,4);
|
|
|
/* If bits are 31 - 26 are %110101,
|
/* If bits are 31 - 26 are %110101,
|
and bits 20 - 16 are %00001,
|
and bits 20 - 16 are %00001,
|
and bits 15 - 11 are %00010,
|
and bits 15 - 11 are %00010,
|
then our frame pointer lies at the offset specified
|
then our frame pointer lies at the offset specified
|
by bits [25-21][10-0]. */
|
by bits [25-21][10-0]. */
|
|
|
int code = insn >> 26;
|
int code = insn >> 26;
|
int r1 = (insn & 0x001F0000) >> 16;
|
int r1 = (insn & 0x001F0000) >> 16;
|
int r2 = (insn & 0x0000F800) >> 11;
|
int r2 = (insn & 0x0000F800) >> 11;
|
int idx_h = (insn & 0x03E00000) >> 10;
|
int idx_h = (insn & 0x03E00000) >> 10;
|
int idx = (insn & 0x000007FF) | idx_h;
|
int idx = (insn & 0x000007FF) | idx_h;
|
|
|
if(code == 0x35 && r1 == 1 && r2 == 2)
|
if(code == 0x35 && r1 == 1 && r2 == 2)
|
{
|
{
|
offset = idx - frame_size;
|
offset = idx - frame_size;
|
fp = read_memory_integer (frame->frame + offset, 4);
|
fp = read_memory_integer (frame->frame + offset, 4);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
if (USE_GENERIC_DUMMY_FRAMES)
|
if (USE_GENERIC_DUMMY_FRAMES)
|
{
|
{
|
CORE_ADDR fpp, lr;
|
CORE_ADDR fpp, lr;
|
|
|
lr = read_register (LR_REGNUM);
|
lr = read_register (LR_REGNUM);
|
if (lr == entry_point_address ())
|
if (lr == entry_point_address ())
|
if (fp != 0 && (fpp = read_memory_integer (fp, 4)) != 0)
|
if (fp != 0 && (fpp = read_memory_integer (fp, 4)) != 0)
|
if (PC_IN_CALL_DUMMY (lr, fpp, fpp))
|
if (PC_IN_CALL_DUMMY (lr, fpp, fpp))
|
return fpp;
|
return fpp;
|
}
|
}
|
|
|
return fp;
|
return fp;
|
}
|
}
|
|
|
/* The code to store, into a struct frame_saved_regs,
|
/* The code to store, into a struct frame_saved_regs,
|
the addresses of the saved registers of frame described by FRAME_INFO.
|
the addresses of the saved registers of frame described by FRAME_INFO.
|
This includes special registers such as pc and fp saved in special
|
This includes special registers such as pc and fp saved in special
|
ways in the stack frame. sp is even more special:
|
ways in the stack frame. sp is even more special:
|
the address we return for it IS the sp for the next frame. */
|
the address we return for it IS the sp for the next frame. */
|
void
|
void
|
or1k_init_saved_regs (struct frame_info *fi)
|
or1k_init_saved_regs (struct frame_info *fi)
|
{
|
{
|
int i;
|
int i;
|
CORE_ADDR frame_addr;
|
CORE_ADDR frame_addr;
|
CORE_ADDR func_pc = get_pc_function_start ((fi)->pc) + FUNCTION_START_OFFSET;
|
CORE_ADDR func_pc = get_pc_function_start ((fi)->pc) + FUNCTION_START_OFFSET;
|
int pc_found = 0;
|
int pc_found = 0;
|
|
|
frame_saved_regs_zalloc (fi);
|
frame_saved_regs_zalloc (fi);
|
|
|
/* Skip prologue sets frame_size and register frame save offsets
|
/* Skip prologue sets frame_size and register frame save offsets
|
which will both be used shortly. */
|
which will both be used shortly. */
|
or1k_skip_prologue (func_pc, fi);
|
or1k_skip_prologue (func_pc, fi);
|
|
|
/* If the frame_size is less than 0, we have hit an assembly
|
/* If the frame_size is less than 0, we have hit an assembly
|
routine which we can't traverse beyond. Let's give up here,
|
routine which we can't traverse beyond. Let's give up here,
|
because attempting to continue will only lead to trouble. */
|
because attempting to continue will only lead to trouble. */
|
if(fi->extra_info->frame_size < 0)
|
if(fi->extra_info->frame_size < 0)
|
{
|
{
|
printf("Found a function without a prologue at 0x%08x\n",func_pc);
|
printf("Found a function without a prologue at 0x%08x\n",func_pc);
|
printf("Frame pc was at 0x%08x\n",fi->pc);
|
printf("Frame pc was at 0x%08x\n",fi->pc);
|
return;
|
return;
|
}
|
}
|
|
|
for (i = 0; i < NUM_GPR_REGS + NUM_VF_REGS; i++)
|
for (i = 0; i < NUM_GPR_REGS + NUM_VF_REGS; i++)
|
if (fi->extra_info->saved_reg_frame_offset[i] >= 0)
|
if (fi->extra_info->saved_reg_frame_offset[i] >= 0)
|
fi->saved_regs[i] =
|
fi->saved_regs[i] =
|
fi->frame + (fi->extra_info->saved_reg_frame_offset[i] - fi->extra_info->frame_size);
|
fi->frame + (fi->extra_info->saved_reg_frame_offset[i] - fi->extra_info->frame_size);
|
|
|
/* We want to make sure we fill in the PC with the value of the
|
/* We want to make sure we fill in the PC with the value of the
|
r9 register from the NEXT frame, and that the value of r1 is
|
r9 register from the NEXT frame, and that the value of r1 is
|
the correct value of r1 for the next frame, which can be
|
the correct value of r1 for the next frame, which can be
|
calculated by adding the frame_size to the frame pointer. */
|
calculated by adding the frame_size to the frame pointer. */
|
fi->saved_regs[1] = fi->frame - fi->extra_info->frame_size;
|
fi->saved_regs[1] = fi->frame - fi->extra_info->frame_size;
|
|
|
if(fi->saved_regs[LR_REGNUM])
|
if(fi->saved_regs[LR_REGNUM])
|
fi->saved_regs[PC_REGNUM] = read_memory_integer(fi->saved_regs[LR_REGNUM],4);
|
fi->saved_regs[PC_REGNUM] = read_memory_integer(fi->saved_regs[LR_REGNUM],4);
|
else
|
else
|
fi->saved_regs[PC_REGNUM] = read_register(LR_REGNUM);
|
fi->saved_regs[PC_REGNUM] = read_register(LR_REGNUM);
|
}
|
}
|
|
|
static CORE_ADDR
|
static CORE_ADDR
|
read_next_frame_reg (fi, regno)
|
read_next_frame_reg (fi, regno)
|
struct frame_info *fi;
|
struct frame_info *fi;
|
int regno;
|
int regno;
|
{
|
{
|
for (; fi; fi = fi->next)
|
for (; fi; fi = fi->next)
|
{
|
{
|
/* We have to get the saved sp from the sigcontext
|
/* We have to get the saved sp from the sigcontext
|
if it is a signal handler frame. */
|
if it is a signal handler frame. */
|
if (regno == SP_REGNUM && !fi->signal_handler_caller)
|
if (regno == SP_REGNUM && !fi->signal_handler_caller)
|
return fi->frame;
|
return fi->frame;
|
else
|
else
|
{
|
{
|
if (fi->saved_regs == NULL)
|
if (fi->saved_regs == NULL)
|
or1k_init_saved_regs (fi);
|
or1k_init_saved_regs (fi);
|
if (fi->saved_regs[regno])
|
if (fi->saved_regs[regno])
|
{
|
{
|
if(regno == SP_REGNUM || regno == PC_REGNUM)
|
if(regno == SP_REGNUM || regno == PC_REGNUM)
|
return fi->saved_regs[regno];
|
return fi->saved_regs[regno];
|
else
|
else
|
return read_memory_integer (ADDR_BITS_REMOVE (fi->saved_regs[regno]), OR1K_GPR_REGSIZE);
|
return read_memory_integer (ADDR_BITS_REMOVE (fi->saved_regs[regno]), OR1K_GPR_REGSIZE);
|
}
|
}
|
}
|
}
|
}
|
}
|
return read_register (regno);
|
return read_register (regno);
|
}
|
}
|
|
|
/* Find the caller of this frame. We do this by seeing if LR_REGNUM
|
/* Find the caller of this frame. We do this by seeing if LR_REGNUM
|
is saved in the stack anywhere, otherwise we get it from the
|
is saved in the stack anywhere, otherwise we get it from the
|
registers. */
|
registers. */
|
|
|
CORE_ADDR
|
CORE_ADDR
|
or1k_frame_saved_pc (fi)
|
or1k_frame_saved_pc (fi)
|
struct frame_info *fi;
|
struct frame_info *fi;
|
{
|
{
|
CORE_ADDR saved_pc;
|
CORE_ADDR saved_pc;
|
|
|
/* We have to get the saved pc from the sigcontext
|
/* We have to get the saved pc from the sigcontext
|
if it is a signal handler frame. */
|
if it is a signal handler frame. */
|
if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame))
|
if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame))
|
saved_pc = read_memory_integer (fi->frame, OR1K_GPR_REGSIZE);
|
saved_pc = read_memory_integer (fi->frame, OR1K_GPR_REGSIZE);
|
else
|
else
|
saved_pc = read_next_frame_reg (fi, PC_REGNUM);
|
saved_pc = read_next_frame_reg (fi, PC_REGNUM);
|
|
|
return ADDR_BITS_REMOVE (saved_pc);
|
return ADDR_BITS_REMOVE (saved_pc);
|
}
|
}
|
|
|
/* Discard from the stack the innermost frame, restoring all registers. */
|
/* Discard from the stack the innermost frame, restoring all registers. */
|
|
|
void
|
void
|
or1k_pop_frame ()
|
or1k_pop_frame ()
|
{
|
{
|
register int regnum;
|
register int regnum;
|
struct frame_info *frame = get_current_frame ();
|
struct frame_info *frame = get_current_frame ();
|
CORE_ADDR new_sp = FRAME_FP (frame);
|
CORE_ADDR new_sp = FRAME_FP (frame);
|
|
|
write_register (PC_REGNUM, FRAME_SAVED_PC (frame));
|
write_register (PC_REGNUM, FRAME_SAVED_PC (frame));
|
if (frame->saved_regs == NULL)
|
if (frame->saved_regs == NULL)
|
or1k_init_saved_regs (frame);
|
or1k_init_saved_regs (frame);
|
for (regnum = 0; regnum < NUM_REGS; regnum++) {
|
for (regnum = 0; regnum < NUM_REGS; regnum++) {
|
if (regnum != SP_REGNUM && regnum != PC_REGNUM
|
if (regnum != SP_REGNUM && regnum != PC_REGNUM
|
&& frame->saved_regs[regnum] >= 0)
|
&& frame->saved_regs[regnum] >= 0)
|
write_register (regnum,
|
write_register (regnum,
|
read_memory_integer (frame->saved_regs[regnum],
|
read_memory_integer (frame->saved_regs[regnum],
|
OR1K_GPR_REGSIZE));
|
OR1K_GPR_REGSIZE));
|
}
|
}
|
write_register (SP_REGNUM, new_sp);
|
write_register (SP_REGNUM, new_sp);
|
flush_cached_frames ();
|
flush_cached_frames ();
|
}
|
}
|
|
|
CORE_ADDR
|
CORE_ADDR
|
or1k_push_arguments (nargs, args, sp, struct_return, struct_addr)
|
or1k_push_arguments (nargs, args, sp, struct_return, struct_addr)
|
int nargs;
|
int nargs;
|
value_ptr *args;
|
value_ptr *args;
|
CORE_ADDR sp;
|
CORE_ADDR sp;
|
int struct_return;
|
int struct_return;
|
CORE_ADDR struct_addr;
|
CORE_ADDR struct_addr;
|
{
|
{
|
int argreg;
|
int argreg;
|
int float_argreg;
|
int float_argreg;
|
int argnum;
|
int argnum;
|
int len = 0;
|
int len = 0;
|
int stack_offset = 0;
|
int stack_offset = 0;
|
|
|
/* Initialize the integer and float register pointers. */
|
/* Initialize the integer and float register pointers. */
|
argreg = A0_REGNUM;
|
argreg = A0_REGNUM;
|
float_argreg = VFA0_REGNUM;
|
float_argreg = VFA0_REGNUM;
|
|
|
/* The struct_return pointer occupies the RV value register. */
|
/* The struct_return pointer occupies the RV value register. */
|
if (struct_return)
|
if (struct_return)
|
write_register (RV_REGNUM, struct_addr);
|
write_register (RV_REGNUM, struct_addr);
|
|
|
/* Now load as many as possible of the first arguments into
|
/* Now load as many as possible of the first arguments into
|
registers, and push the rest onto the stack. Loop thru args
|
registers, and push the rest onto the stack. Loop thru args
|
from first to last. */
|
from first to last. */
|
for (argnum = 0; argnum < nargs; argnum++)
|
for (argnum = 0; argnum < nargs; argnum++)
|
{
|
{
|
char *val;
|
char *val;
|
char valbuf[MAX_REGISTER_RAW_SIZE];
|
char valbuf[MAX_REGISTER_RAW_SIZE];
|
value_ptr arg = args[argnum];
|
value_ptr arg = args[argnum];
|
struct type *arg_type = check_typedef (VALUE_TYPE (arg));
|
struct type *arg_type = check_typedef (VALUE_TYPE (arg));
|
int len = TYPE_LENGTH (arg_type);
|
int len = TYPE_LENGTH (arg_type);
|
enum type_code typecode = TYPE_CODE (arg_type);
|
enum type_code typecode = TYPE_CODE (arg_type);
|
|
|
/* The EABI passes structures that do not fit in a register by
|
/* The EABI passes structures that do not fit in a register by
|
reference. In all other cases, pass the structure by value. */
|
reference. In all other cases, pass the structure by value. */
|
if (len > OR1K_GPR_REGSIZE &&
|
if (len > OR1K_GPR_REGSIZE &&
|
(typecode == TYPE_CODE_STRUCT || typecode == TYPE_CODE_UNION))
|
(typecode == TYPE_CODE_STRUCT || typecode == TYPE_CODE_UNION))
|
{
|
{
|
store_address (valbuf, OR1K_GPR_REGSIZE, VALUE_ADDRESS (arg));
|
store_address (valbuf, OR1K_GPR_REGSIZE, VALUE_ADDRESS (arg));
|
typecode = TYPE_CODE_PTR;
|
typecode = TYPE_CODE_PTR;
|
len = OR1K_GPR_REGSIZE;
|
len = OR1K_GPR_REGSIZE;
|
val = valbuf;
|
val = valbuf;
|
}
|
}
|
else
|
else
|
{
|
{
|
val = (char *) VALUE_CONTENTS (arg);
|
val = (char *) VALUE_CONTENTS (arg);
|
|
|
if (typecode == TYPE_CODE_FLT
|
if (typecode == TYPE_CODE_FLT
|
/* Doubles are not stored in regs on 32b target. */
|
/* Doubles are not stored in regs on 32b target. */
|
&& len <= OR1K_VF_REGSIZE
|
&& len <= OR1K_VF_REGSIZE
|
&& OR1K_VF_PRESENT)
|
&& OR1K_VF_PRESENT)
|
{
|
{
|
if (float_argreg <= OR1K_LAST_FP_ARG_REGNUM)
|
if (float_argreg <= OR1K_LAST_FP_ARG_REGNUM)
|
{
|
{
|
CORE_ADDR regval = extract_address (val, len);
|
CORE_ADDR regval = extract_address (val, len);
|
write_register (float_argreg++, regval);
|
write_register (float_argreg++, regval);
|
}
|
}
|
else
|
else
|
{
|
{
|
write_memory ((CORE_ADDR) sp, val, OR1K_VF_REGSIZE);
|
write_memory ((CORE_ADDR) sp, val, OR1K_VF_REGSIZE);
|
sp -= OR1K_STACK_ALIGN;
|
sp -= OR1K_STACK_ALIGN;
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
if (argreg <= OR1K_LAST_ARG_REGNUM)
|
if (argreg <= OR1K_LAST_ARG_REGNUM)
|
{
|
{
|
CORE_ADDR regval = extract_address (val, len);
|
CORE_ADDR regval = extract_address (val, len);
|
write_register (argreg++, regval);
|
write_register (argreg++, regval);
|
}
|
}
|
else
|
else
|
{
|
{
|
write_memory ((CORE_ADDR) sp, val, OR1K_GPR_REGSIZE);
|
write_memory ((CORE_ADDR) sp, val, OR1K_GPR_REGSIZE);
|
sp -= OR1K_STACK_ALIGN;
|
sp -= OR1K_STACK_ALIGN;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Return adjusted stack pointer. */
|
/* Return adjusted stack pointer. */
|
return sp;
|
return sp;
|
}
|
}
|
|
|
/* Return nonzero when instruction has delay slot. */
|
/* Return nonzero when instruction has delay slot. */
|
|
|
int
|
int
|
is_delayed (insn)
|
is_delayed (insn)
|
unsigned long insn;
|
unsigned long insn;
|
{
|
{
|
int index;
|
int index;
|
index = insn_decode (insn);
|
index = insn_decode (insn);
|
return or32_opcodes[index].flags & OR32_IF_DELAY;
|
return or32_opcodes[index].flags & OR32_IF_DELAY;
|
}
|
}
|
|
|
int
|
int
|
or1k_step_skips_delay (pc)
|
or1k_step_skips_delay (pc)
|
CORE_ADDR pc;
|
CORE_ADDR pc;
|
{
|
{
|
char buf[OR1K_INSTLEN];
|
char buf[OR1K_INSTLEN];
|
|
|
if (target_read_memory (pc, buf, OR1K_INSTLEN) != 0)
|
if (target_read_memory (pc, buf, OR1K_INSTLEN) != 0)
|
/* If error reading memory, guess that it is not a delayed branch. */
|
/* If error reading memory, guess that it is not a delayed branch. */
|
return 0;
|
return 0;
|
return is_delayed ((unsigned long) extract_unsigned_integer (buf, OR1K_INSTLEN));
|
return is_delayed ((unsigned long) extract_unsigned_integer (buf, OR1K_INSTLEN));
|
}
|
}
|
|
|
CORE_ADDR
|
CORE_ADDR
|
or1k_push_return_address (pc, sp)
|
or1k_push_return_address (pc, sp)
|
CORE_ADDR pc;
|
CORE_ADDR pc;
|
CORE_ADDR sp;
|
CORE_ADDR sp;
|
{
|
{
|
/* Set the return address register to point to the entry
|
/* Set the return address register to point to the entry
|
point of the program, where a breakpoint lies in wait. */
|
point of the program, where a breakpoint lies in wait. */
|
write_register (LR_REGNUM, CALL_DUMMY_ADDRESS ());
|
write_register (LR_REGNUM, CALL_DUMMY_ADDRESS ());
|
return sp;
|
return sp;
|
}
|
}
|
|
|
/* Parses args for spr name and puts result into group and index. */
|
/* Parses args for spr name and puts result into group and index. */
|
|
|
static char *
|
static char *
|
parse_spr_params (args, group, index)
|
parse_spr_params (args, group, index)
|
char *args;
|
char *args;
|
int *group, *index;
|
int *group, *index;
|
{
|
{
|
*index = -1;
|
*index = -1;
|
if (args)
|
if (args)
|
{
|
{
|
int i;
|
int i;
|
char *ptr_c;
|
char *ptr_c;
|
|
|
/* Check if group number was supplied. */
|
/* Check if group number was supplied. */
|
ptr_c = args;
|
ptr_c = args;
|
while (*ptr_c != ' ' && *ptr_c != 0)
|
while (*ptr_c != ' ' && *ptr_c != 0)
|
ptr_c++;
|
ptr_c++;
|
*ptr_c = 0;
|
*ptr_c = 0;
|
|
|
*group = (int) strtoul (args, &ptr_c, 0);
|
*group = (int) strtoul (args, &ptr_c, 0);
|
if (*ptr_c != 0)
|
if (*ptr_c != 0)
|
{
|
{
|
*group = OR1K_NUM_SPR_GROUPS;
|
*group = OR1K_NUM_SPR_GROUPS;
|
|
|
/* check for group name */
|
/* check for group name */
|
for (i = 0; i < OR1K_NUM_SPR_GROUPS; i++)
|
for (i = 0; i < OR1K_NUM_SPR_GROUPS; i++)
|
if (strcasecmp (or1k_group_names[i], args) == 0)
|
if (strcasecmp (or1k_group_names[i], args) == 0)
|
{
|
{
|
*group = i;
|
*group = i;
|
break;
|
break;
|
}
|
}
|
|
|
/* Invalid group => check all register names in all groups. */
|
/* Invalid group => check all register names in all groups. */
|
if (*group >= OR1K_NUM_SPR_GROUPS)
|
if (*group >= OR1K_NUM_SPR_GROUPS)
|
{
|
{
|
for (i = 0; i < OR1K_NUM_SPR_GROUPS; i++)
|
for (i = 0; i < OR1K_NUM_SPR_GROUPS; i++)
|
{
|
{
|
int regno;
|
int regno;
|
regno = or1k_regno_from_name (i, args);
|
regno = or1k_regno_from_name (i, args);
|
if (regno >= 0)
|
if (regno >= 0)
|
{
|
{
|
*group = i;
|
*group = i;
|
*index = regno;
|
*index = regno;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
if (*group < 0 || *group >= OR1K_NUM_SPR_GROUPS)
|
if (*group < 0 || *group >= OR1K_NUM_SPR_GROUPS)
|
error ("Invalid group or register.\n");
|
error ("Invalid group or register.\n");
|
|
|
if (*index < 0)
|
if (*index < 0)
|
{
|
{
|
printf ("a'%s'\n", args);
|
printf ("a'%s'\n", args);
|
args += strlen(args) + 1;
|
args += strlen(args) + 1;
|
printf ("b'%s'\n", args);
|
printf ("b'%s'\n", args);
|
if (*args != 0)
|
if (*args != 0)
|
{
|
{
|
ptr_c = args;
|
ptr_c = args;
|
while (*ptr_c != ' ' && *ptr_c != 0)
|
while (*ptr_c != ' ' && *ptr_c != 0)
|
ptr_c++;
|
ptr_c++;
|
*ptr_c = 0;
|
*ptr_c = 0;
|
|
|
*index = (int) strtoul (args, &ptr_c, 0);
|
*index = (int) strtoul (args, &ptr_c, 0);
|
if (*ptr_c != 0)
|
if (*ptr_c != 0)
|
*index = or1k_regno_from_name (*group, args);
|
*index = or1k_regno_from_name (*group, args);
|
}
|
}
|
|
|
if (*index < 0)
|
if (*index < 0)
|
{
|
{
|
printf_filtered ("No register supplied. Valid registers are:\n");
|
printf_filtered ("No register supplied. Valid registers are:\n");
|
for (i = 0; i < or1k_spr_valid_aliases[*group]; i++)
|
for (i = 0; i < or1k_spr_valid_aliases[*group]; i++)
|
{
|
{
|
char reg_name[16];
|
char reg_name[16];
|
char *gen_name = or1k_spr_register_name (SPR_REG(*group, i));
|
char *gen_name = or1k_spr_register_name (SPR_REG(*group, i));
|
sprintf (reg_name, "SPR%i_%i", *group, i);
|
sprintf (reg_name, "SPR%i_%i", *group, i);
|
if (strcmp (reg_name, gen_name) != 0)
|
if (strcmp (reg_name, gen_name) != 0)
|
printf_filtered ("%s\t", gen_name);
|
printf_filtered ("%s\t", gen_name);
|
}
|
}
|
printf_filtered ("\n");
|
printf_filtered ("\n");
|
return args + strlen(args) + 1;
|
return args + strlen(args) + 1;
|
}
|
}
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
/* No parameters - print groups */
|
/* No parameters - print groups */
|
int i;
|
int i;
|
printf_filtered ("No parameter supplied. Valid groups are:\n");
|
printf_filtered ("No parameter supplied. Valid groups are:\n");
|
for (i = 0; i < OR1K_NUM_SPR_GROUPS; i++)
|
for (i = 0; i < OR1K_NUM_SPR_GROUPS; i++)
|
printf_filtered ("%s\t", or1k_group_names[i]);
|
printf_filtered ("%s\t", or1k_group_names[i]);
|
printf_filtered ("\nSingle register name or register name or number after the group can be also supplied.\n");
|
printf_filtered ("\nSingle register name or register name or number after the group can be also supplied.\n");
|
return args;
|
return args;
|
}
|
}
|
return args + strlen(args) + 1;
|
return args + strlen(args) + 1;
|
}
|
}
|
|
|
/* SPR register info. */
|
/* SPR register info. */
|
|
|
void
|
void
|
info_spr_command (args, from_tty)
|
info_spr_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
int group, index;
|
int group, index;
|
parse_spr_params (args, &group, &index);
|
parse_spr_params (args, &group, &index);
|
if (index >= 0)
|
if (index >= 0)
|
{
|
{
|
unsigned long value = or1k_read_spr_reg (SPR_REG(group, index));
|
unsigned long value = or1k_read_spr_reg (SPR_REG(group, index));
|
printf_unfiltered ("%s.%s = SPR%i_%i = %i(%x)\n", or1k_group_names[group],
|
printf_unfiltered ("%s.%s = SPR%i_%i = %i(%x)\n", or1k_group_names[group],
|
or1k_spr_register_name (SPR_REG(group, index)), group, index, value, value);
|
or1k_spr_register_name (SPR_REG(group, index)), group, index, value, value);
|
}
|
}
|
}
|
}
|
|
|
void
|
void
|
info_trace_command (args, from_tty)
|
info_trace_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
unsigned char i;
|
unsigned char i;
|
int j;
|
int j;
|
printf_filtered (" add insn res time\n");
|
printf_filtered (" add insn res time\n");
|
i = or1k_read_spr_reg (SPR_REG(6, 255));
|
i = or1k_read_spr_reg (SPR_REG(6, 255));
|
for (j = 0; j < 256; j++, i++)
|
for (j = 0; j < 256; j++, i++)
|
printf_filtered ("%03d %03u %08x %08x %08x %08x\n", 255 - j, i,
|
printf_filtered ("%03d %03u %08x %08x %08x %08x\n", 255 - j, i,
|
or1k_read_spr_reg (SPR_REG(6, 0x100 + i)),
|
or1k_read_spr_reg (SPR_REG(6, 0x100 + i)),
|
or1k_read_spr_reg (SPR_REG(6, 0x200 + i)),
|
or1k_read_spr_reg (SPR_REG(6, 0x200 + i)),
|
or1k_read_spr_reg (SPR_REG(6, 0x300 + i)),
|
or1k_read_spr_reg (SPR_REG(6, 0x300 + i)),
|
or1k_read_spr_reg (SPR_REG(6, 0x400 + i)));
|
or1k_read_spr_reg (SPR_REG(6, 0x400 + i)));
|
|
|
}
|
}
|
|
|
/* Set SPR register. */
|
/* Set SPR register. */
|
|
|
void
|
void
|
spr_command (args, from_tty)
|
spr_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
int group, index;
|
int group, index;
|
char *nargs = parse_spr_params (args, &group, &index);
|
char *nargs = parse_spr_params (args, &group, &index);
|
if (index >= 0)
|
if (index >= 0)
|
{
|
{
|
unsigned long prev;
|
unsigned long prev;
|
unsigned long value;
|
unsigned long value;
|
char *ptr_c;
|
char *ptr_c;
|
|
|
/* Any arguments left? */
|
/* Any arguments left? */
|
if (args + strlen(args) >= nargs)
|
if (args + strlen(args) >= nargs)
|
error ("Invalid register value.");
|
error ("Invalid register value.");
|
|
|
prev = or1k_read_spr_reg (SPR_REG(group, index));
|
prev = or1k_read_spr_reg (SPR_REG(group, index));
|
|
|
ptr_c = nargs;
|
ptr_c = nargs;
|
while (*ptr_c != ' ' && *ptr_c != 0)
|
while (*ptr_c != ' ' && *ptr_c != 0)
|
ptr_c++;
|
ptr_c++;
|
*ptr_c = 0;
|
*ptr_c = 0;
|
value = strtoul (nargs, &ptr_c, 0);
|
value = strtoul (nargs, &ptr_c, 0);
|
if (*ptr_c != 0)
|
if (*ptr_c != 0)
|
error ("Invalid register value.");
|
error ("Invalid register value.");
|
or1k_write_spr_reg (SPR_REG(group, index), value);
|
or1k_write_spr_reg (SPR_REG(group, index), value);
|
printf_unfiltered ("%s.%s (SPR%i_%i) set to %i(%x), was:%i(%x)\n", or1k_group_names[group],
|
printf_unfiltered ("%s.%s (SPR%i_%i) set to %i(%x), was:%i(%x)\n", or1k_group_names[group],
|
or1k_spr_register_name (SPR_REG(group, index)), group, index,
|
or1k_spr_register_name (SPR_REG(group, index)), group, index,
|
value, value, prev, prev);
|
value, value, prev, prev);
|
}
|
}
|
}
|
}
|
|
|
/* Calls extended command on target. */
|
/* Calls extended command on target. */
|
|
|
void
|
void
|
sim_command (args, from_tty)
|
sim_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
or1k_sim_cmd (args, from_tty);
|
or1k_sim_cmd (args, from_tty);
|
}
|
}
|
|
|
|
|
static union exp_element exp_error;
|
static union exp_element exp_error;
|
|
|
/* Parses compare variable and returns it into ct. */
|
/* Parses compare variable and returns it into ct. */
|
|
|
union exp_element * or1k_parse_ct (exp, ct)
|
union exp_element * or1k_parse_ct (exp, ct)
|
union exp_element *exp;
|
union exp_element *exp;
|
int *ct;
|
int *ct;
|
{
|
{
|
int i;
|
int i;
|
if (exp->opcode != OP_INTERNALVAR)
|
if (exp->opcode != OP_INTERNALVAR)
|
error ("Valid lvalue expected.");
|
error ("Valid lvalue expected.");
|
exp++;
|
exp++;
|
|
|
for (i = 1; i < NUM_CT_NAMES; i++)
|
for (i = 1; i < NUM_CT_NAMES; i++)
|
if (strcasecmp (compare_to_names[i], exp->internalvar->name) == 0) break;
|
if (strcasecmp (compare_to_names[i], exp->internalvar->name) == 0) break;
|
|
|
if (i >= NUM_CT_NAMES)
|
if (i >= NUM_CT_NAMES)
|
error ("Invalid compare to operand.");
|
error ("Invalid compare to operand.");
|
*ct = i;
|
*ct = i;
|
exp++;
|
exp++;
|
|
|
if (exp->opcode != OP_INTERNALVAR)
|
if (exp->opcode != OP_INTERNALVAR)
|
return &exp_error;
|
return &exp_error;
|
exp++;
|
exp++;
|
return exp;
|
return exp;
|
}
|
}
|
|
|
/* Parses compare value and returns it into cv. */
|
/* Parses compare value and returns it into cv. */
|
|
|
union exp_element * or1k_parse_cv (exp, cv)
|
union exp_element * or1k_parse_cv (exp, cv)
|
union exp_element *exp;
|
union exp_element *exp;
|
unsigned int *cv;
|
unsigned int *cv;
|
{
|
{
|
switch (exp->opcode)
|
switch (exp->opcode)
|
{
|
{
|
case UNOP_IND:
|
case UNOP_IND:
|
exp++;
|
exp++;
|
exp = or1k_parse_cv (exp, cv);
|
exp = or1k_parse_cv (exp, cv);
|
*cv = or1k_fetch_word (*cv);
|
*cv = or1k_fetch_word (*cv);
|
break;
|
break;
|
case OP_LONG:
|
case OP_LONG:
|
exp += 2;
|
exp += 2;
|
*cv = exp->longconst;
|
*cv = exp->longconst;
|
exp += 2;
|
exp += 2;
|
break;
|
break;
|
case OP_REGISTER:
|
case OP_REGISTER:
|
exp++;
|
exp++;
|
*cv = read_register (exp->longconst);
|
*cv = read_register (exp->longconst);
|
exp += 2;
|
exp += 2;
|
break;
|
break;
|
default:
|
default:
|
error ("Value expected.");
|
error ("Value expected.");
|
}
|
}
|
return exp;
|
return exp;
|
}
|
}
|
|
|
/* Parse conditional.
|
/* Parse conditional.
|
Puts freshly allocated array of matchpoints into match. */
|
Puts freshly allocated array of matchpoints into match. */
|
|
|
union exp_element *
|
union exp_element *
|
or1k_parse_cond (exp, match, nmatch)
|
or1k_parse_cond (exp, match, nmatch)
|
union exp_element *exp;
|
union exp_element *exp;
|
struct matchpoint **match;
|
struct matchpoint **match;
|
int *nmatch;
|
int *nmatch;
|
{
|
{
|
unsigned int ct;
|
unsigned int ct;
|
*match = (struct matchpoint *) malloc (sizeof (struct matchpoint));
|
*match = (struct matchpoint *) malloc (sizeof (struct matchpoint));
|
*nmatch = 1;
|
*nmatch = 1;
|
switch (exp->opcode)
|
switch (exp->opcode)
|
{
|
{
|
case BINOP_EQUAL:
|
case BINOP_EQUAL:
|
(*match)->dcr.cc = CC_EQUAL;
|
(*match)->dcr.cc = CC_EQUAL;
|
break;
|
break;
|
case BINOP_NOTEQUAL:
|
case BINOP_NOTEQUAL:
|
(*match)->dcr.cc = CC_NEQUAL;
|
(*match)->dcr.cc = CC_NEQUAL;
|
break;
|
break;
|
case BINOP_LESS:
|
case BINOP_LESS:
|
(*match)->dcr.cc = CC_LESS;
|
(*match)->dcr.cc = CC_LESS;
|
break;
|
break;
|
case BINOP_GTR:
|
case BINOP_GTR:
|
(*match)->dcr.cc = CC_GREAT;
|
(*match)->dcr.cc = CC_GREAT;
|
break;
|
break;
|
case BINOP_LEQ:
|
case BINOP_LEQ:
|
(*match)->dcr.cc = CC_LESSE;
|
(*match)->dcr.cc = CC_LESSE;
|
break;
|
break;
|
case BINOP_GEQ:
|
case BINOP_GEQ:
|
(*match)->dcr.cc = CC_GREATE;
|
(*match)->dcr.cc = CC_GREATE;
|
break;
|
break;
|
case BINOP_BITWISE_AND:
|
case BINOP_BITWISE_AND:
|
(*match)->dcr.cc = CC_MASKED;
|
(*match)->dcr.cc = CC_MASKED;
|
break;
|
break;
|
default:
|
default:
|
return &exp_error;
|
return &exp_error;
|
}
|
}
|
|
|
exp++;
|
exp++;
|
(*match)->dcr.dp = 1;
|
(*match)->dcr.dp = 1;
|
(*match)->dcr.sc = 0;
|
(*match)->dcr.sc = 0;
|
if (exp->opcode == OP_INTERNALVAR)
|
if (exp->opcode == OP_INTERNALVAR)
|
{
|
{
|
exp = or1k_parse_ct (exp, &ct);
|
exp = or1k_parse_ct (exp, &ct);
|
exp = or1k_parse_cv (exp, &(*match)->dvr);
|
exp = or1k_parse_cv (exp, &(*match)->dvr);
|
}
|
}
|
else
|
else
|
{
|
{
|
exp = or1k_parse_cv (exp, &(*match)->dvr);
|
exp = or1k_parse_cv (exp, &(*match)->dvr);
|
exp = or1k_parse_ct (exp, &ct);
|
exp = or1k_parse_ct (exp, &ct);
|
}
|
}
|
|
|
(*match)->dcr.ct = ct;
|
(*match)->dcr.ct = ct;
|
(*match)->chain_type = CHAINING_NONE;
|
(*match)->chain_type = CHAINING_NONE;
|
(*match)->cause_breakpoint = 0;
|
(*match)->cause_breakpoint = 0;
|
return exp;
|
return exp;
|
}
|
}
|
|
|
/* Parses expression with && or || operators.
|
/* Parses expression with && or || operators.
|
Puts freshly allocated array of matchpoints into match.
|
Puts freshly allocated array of matchpoints into match.
|
valid & 1: && is allowed,
|
valid & 1: && is allowed,
|
valid & 2: || is allowed. */
|
valid & 2: || is allowed. */
|
|
|
union exp_element *
|
union exp_element *
|
or1k_parse_any (exp, match, nmatch, valid)
|
or1k_parse_any (exp, match, nmatch, valid)
|
union exp_element *exp;
|
union exp_element *exp;
|
struct matchpoint **match;
|
struct matchpoint **match;
|
int *nmatch;
|
int *nmatch;
|
int valid;
|
int valid;
|
{
|
{
|
union exp_element *tmp;
|
union exp_element *tmp;
|
int first_and_only = 0, first_or_only = 0;
|
int first_and_only = 0, first_or_only = 0;
|
struct matchpoint *tmp_match1, *tmp_match2, *tmpm;
|
struct matchpoint *tmp_match1, *tmp_match2, *tmpm;
|
int tmp_nmatch1, tmp_nmatch2, tmpn;
|
int tmp_nmatch1, tmp_nmatch2, tmpn;
|
|
|
switch (exp->opcode)
|
switch (exp->opcode)
|
{
|
{
|
case BINOP_LOGICAL_AND:
|
case BINOP_LOGICAL_AND:
|
if (!(valid & 1))
|
if (!(valid & 1))
|
return &exp_error;
|
return &exp_error;
|
exp++;
|
exp++;
|
|
|
/* Parse first argument. */
|
/* Parse first argument. */
|
tmp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, 1);
|
tmp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, 1);
|
if (tmp == &exp_error)
|
if (tmp == &exp_error)
|
exp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, valid);
|
exp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, valid);
|
else
|
else
|
{
|
{
|
/* and_only successful */
|
/* and_only successful */
|
exp = tmp;
|
exp = tmp;
|
first_and_only = 1;
|
first_and_only = 1;
|
}
|
}
|
if (exp == &exp_error)
|
if (exp == &exp_error)
|
return &exp_error;
|
return &exp_error;
|
|
|
/* Parse second argument. */
|
/* Parse second argument. */
|
if (first_and_only)
|
if (first_and_only)
|
exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, valid);
|
exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, valid);
|
else
|
else
|
exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, 1);
|
exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, 1);
|
|
|
if (exp == &exp_error)
|
if (exp == &exp_error)
|
return &exp_error;
|
return &exp_error;
|
|
|
if (first_and_only)
|
if (first_and_only)
|
{
|
{
|
/* Exchange structures, so that and_only is listed last. */
|
/* Exchange structures, so that and_only is listed last. */
|
struct matchpoint *tmpm = tmp_match1;
|
struct matchpoint *tmpm = tmp_match1;
|
int tmpn = tmp_nmatch1;
|
int tmpn = tmp_nmatch1;
|
tmp_match1 = tmp_match2;
|
tmp_match1 = tmp_match2;
|
tmp_nmatch1 = tmp_nmatch2;
|
tmp_nmatch1 = tmp_nmatch2;
|
tmp_match2 = tmpm;
|
tmp_match2 = tmpm;
|
tmp_nmatch2 = tmpn;
|
tmp_nmatch2 = tmpn;
|
}
|
}
|
|
|
*nmatch = tmp_nmatch1 + tmp_nmatch2;
|
*nmatch = tmp_nmatch1 + tmp_nmatch2;
|
*match = (struct matchpoint *)malloc (*nmatch * sizeof (struct matchpoint));
|
*match = (struct matchpoint *)malloc (*nmatch * sizeof (struct matchpoint));
|
memcpy (*match, tmp_match1, tmp_nmatch1 * sizeof (struct matchpoint));
|
memcpy (*match, tmp_match1, tmp_nmatch1 * sizeof (struct matchpoint));
|
free (tmp_match1);
|
free (tmp_match1);
|
tmp_match2[0].chain_type = CHAINING_AND;
|
tmp_match2[0].chain_type = CHAINING_AND;
|
memcpy (*match + tmp_nmatch1, tmp_match2, tmp_nmatch2 * sizeof (struct matchpoint));
|
memcpy (*match + tmp_nmatch1, tmp_match2, tmp_nmatch2 * sizeof (struct matchpoint));
|
free (tmp_match2);
|
free (tmp_match2);
|
return exp;
|
return exp;
|
|
|
case BINOP_LOGICAL_OR:
|
case BINOP_LOGICAL_OR:
|
if (!(valid & 2))
|
if (!(valid & 2))
|
return &exp_error;
|
return &exp_error;
|
exp++;
|
exp++;
|
|
|
/* Parse first argument. */
|
/* Parse first argument. */
|
tmp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, 2);
|
tmp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, 2);
|
if (tmp == &exp_error)
|
if (tmp == &exp_error)
|
exp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, valid);
|
exp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, valid);
|
else
|
else
|
{
|
{
|
/* and_only successful */
|
/* and_only successful */
|
exp = tmp;
|
exp = tmp;
|
first_or_only = 1;
|
first_or_only = 1;
|
}
|
}
|
if (exp == &exp_error)
|
if (exp == &exp_error)
|
return &exp_error;
|
return &exp_error;
|
|
|
/* Parse second argument. */
|
/* Parse second argument. */
|
if (first_or_only)
|
if (first_or_only)
|
exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, valid);
|
exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, valid);
|
else
|
else
|
exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, 2);
|
exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, 2);
|
|
|
if (exp == &exp_error)
|
if (exp == &exp_error)
|
return &exp_error;
|
return &exp_error;
|
|
|
if (first_or_only)
|
if (first_or_only)
|
{
|
{
|
/* Exchange structures, so that and_only is listed first. */
|
/* Exchange structures, so that and_only is listed first. */
|
struct matchpoint *tmpm = tmp_match1;
|
struct matchpoint *tmpm = tmp_match1;
|
int tmpn = tmp_nmatch1;
|
int tmpn = tmp_nmatch1;
|
tmp_match1 = tmp_match2;
|
tmp_match1 = tmp_match2;
|
tmp_nmatch1 = tmp_nmatch2;
|
tmp_nmatch1 = tmp_nmatch2;
|
tmp_match2 = tmpm;
|
tmp_match2 = tmpm;
|
tmp_nmatch2 = tmpn;
|
tmp_nmatch2 = tmpn;
|
}
|
}
|
|
|
*nmatch = tmp_nmatch1 + tmp_nmatch2;
|
*nmatch = tmp_nmatch1 + tmp_nmatch2;
|
*match = (struct matchpoint *)malloc (*nmatch * sizeof (struct matchpoint));
|
*match = (struct matchpoint *)malloc (*nmatch * sizeof (struct matchpoint));
|
memcpy (*match, tmp_match1, tmp_nmatch1 * sizeof (struct matchpoint));
|
memcpy (*match, tmp_match1, tmp_nmatch1 * sizeof (struct matchpoint));
|
free (tmp_match1);
|
free (tmp_match1);
|
tmp_match2[0].chain_type = CHAINING_OR;
|
tmp_match2[0].chain_type = CHAINING_OR;
|
memcpy (*match + tmp_nmatch1, tmp_match2, tmp_nmatch2 * sizeof (struct matchpoint));
|
memcpy (*match + tmp_nmatch1, tmp_match2, tmp_nmatch2 * sizeof (struct matchpoint));
|
free (tmp_match2);
|
free (tmp_match2);
|
return exp;
|
return exp;
|
|
|
default:
|
default:
|
return or1k_parse_cond (exp, match, nmatch);
|
return or1k_parse_cond (exp, match, nmatch);
|
}
|
}
|
}
|
}
|
|
|
/* Parses sequence of ||s.
|
/* Parses sequence of ||s.
|
Puts freshly allocated array of matchpoints into match. */
|
Puts freshly allocated array of matchpoints into match. */
|
|
|
union exp_element *
|
union exp_element *
|
or1k_parse_or (exp, match, nmatch, set_break)
|
or1k_parse_or (exp, match, nmatch, set_break)
|
union exp_element *exp;
|
union exp_element *exp;
|
struct matchpoint **match;
|
struct matchpoint **match;
|
int *nmatch;
|
int *nmatch;
|
int set_break;
|
int set_break;
|
{
|
{
|
struct matchpoint *tmp_match1, *tmp_match2;
|
struct matchpoint *tmp_match1, *tmp_match2;
|
int tmp_nmatch1, tmp_nmatch2;
|
int tmp_nmatch1, tmp_nmatch2;
|
|
|
switch (exp->opcode)
|
switch (exp->opcode)
|
{
|
{
|
case BINOP_LOGICAL_OR:
|
case BINOP_LOGICAL_OR:
|
exp++;
|
exp++;
|
exp = or1k_parse_or (exp, &tmp_match1, &tmp_nmatch1);
|
exp = or1k_parse_or (exp, &tmp_match1, &tmp_nmatch1);
|
if (exp == &exp_error)
|
if (exp == &exp_error)
|
return &exp_error;
|
return &exp_error;
|
|
|
exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, 3);
|
exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, 3);
|
if (set_break)
|
if (set_break)
|
{
|
{
|
tmp_match1[tmp_nmatch1 - 1].cause_breakpoint = 1;
|
tmp_match1[tmp_nmatch1 - 1].cause_breakpoint = 1;
|
tmp_match2[tmp_nmatch2 - 1].cause_breakpoint = 1;
|
tmp_match2[tmp_nmatch2 - 1].cause_breakpoint = 1;
|
}
|
}
|
*nmatch = tmp_nmatch1 + tmp_nmatch2;
|
*nmatch = tmp_nmatch1 + tmp_nmatch2;
|
*match = (struct matchpoint *)malloc (*nmatch * sizeof (struct matchpoint));
|
*match = (struct matchpoint *)malloc (*nmatch * sizeof (struct matchpoint));
|
memcpy (*match, tmp_match1, tmp_nmatch1 * sizeof (struct matchpoint));
|
memcpy (*match, tmp_match1, tmp_nmatch1 * sizeof (struct matchpoint));
|
free (tmp_match1);
|
free (tmp_match1);
|
memcpy (*match + tmp_nmatch1, tmp_match2, tmp_nmatch2 * sizeof (struct matchpoint));
|
memcpy (*match + tmp_nmatch1, tmp_match2, tmp_nmatch2 * sizeof (struct matchpoint));
|
free (tmp_match2);
|
free (tmp_match2);
|
return exp;
|
return exp;
|
|
|
default:
|
default:
|
return or1k_parse_any (exp, match, nmatch, 3);
|
return or1k_parse_any (exp, match, nmatch, 3);
|
if (set_break)
|
if (set_break)
|
(*match)[*nmatch - 1].cause_breakpoint = 1;
|
(*match)[*nmatch - 1].cause_breakpoint = 1;
|
}
|
}
|
}
|
}
|
|
|
/* Prints single matchpoint from specified struct. */
|
/* Prints single matchpoint from specified struct. */
|
|
|
static void
|
static void
|
print_matchpoint_struct (mp)
|
print_matchpoint_struct (mp)
|
struct matchpoint *mp;
|
struct matchpoint *mp;
|
{
|
{
|
printf_filtered ("%-6s (%i) %u, ON=%i, chain_type=%i, cause break=%i\n", compare_to_names[mp->dcr.ct],
|
printf_filtered ("%-6s (%i) %u, ON=%i, chain_type=%i, cause break=%i\n", compare_to_names[mp->dcr.ct],
|
mp->dcr.cc, mp->dvr, mp->dcr.dp, mp->chain_type, mp->cause_breakpoint);
|
mp->dcr.cc, mp->dvr, mp->dcr.dp, mp->chain_type, mp->cause_breakpoint);
|
}
|
}
|
|
|
/* Build watchpoint(s) based on given structure. */
|
/* Build watchpoint(s) based on given structure. */
|
|
|
static void
|
static void
|
set_matchpoints (match, nmatch)
|
set_matchpoints (match, nmatch)
|
struct matchpoint *match;
|
struct matchpoint *match;
|
int nmatch;
|
int nmatch;
|
{
|
{
|
int i;
|
int i;
|
debug_regs_changed = 1;
|
debug_regs_changed = 1;
|
sift_matchpoints ();
|
sift_matchpoints ();
|
for (i = 0; i < nmatch; i++)
|
for (i = 0; i < nmatch; i++)
|
{
|
{
|
int num = or1k_implementation.num_used_matchpoints;
|
int num = or1k_implementation.num_used_matchpoints;
|
dcr[num] = match[i].dcr;
|
dcr[num] = match[i].dcr;
|
dvr[num] = match[i].dvr;
|
dvr[num] = match[i].dvr;
|
|
|
/* Set chaining bits. */
|
/* Set chaining bits. */
|
dmr1 &= ~(3 << (2 * num));
|
dmr1 &= ~(3 << (2 * num));
|
dmr1 |= match[i].chain_type << (2 * num);
|
dmr1 |= match[i].chain_type << (2 * num);
|
|
|
/* Set watchpoint bits */
|
/* Set watchpoint bits */
|
dmr2 &= 1 << num;
|
dmr2 &= 1 << num;
|
dmr2 |= match[i].cause_breakpoint << num;
|
dmr2 |= match[i].cause_breakpoint << num;
|
matchpoint_user_count[i]++;
|
matchpoint_user_count[i]++;
|
or1k_implementation.num_used_matchpoints++;
|
or1k_implementation.num_used_matchpoints++;
|
}
|
}
|
}
|
}
|
|
|
/* Returns nonzero, if matchpoints [start .. start+nmatch-1] are
|
/* Returns nonzero, if matchpoints [start .. start+nmatch-1] are
|
equal to match record. */
|
equal to match record. */
|
|
|
static int
|
static int
|
matchpoint_matches (start, match, nmatch)
|
matchpoint_matches (start, match, nmatch)
|
int start;
|
int start;
|
struct matchpoint *match;
|
struct matchpoint *match;
|
int nmatch;
|
int nmatch;
|
{
|
{
|
int i;
|
int i;
|
if (nmatch + start >= or1k_implementation.num_matchpoints)
|
if (nmatch + start >= or1k_implementation.num_matchpoints)
|
return 0;
|
return 0;
|
|
|
for (i = 0; i < nmatch; i++)
|
for (i = 0; i < nmatch; i++)
|
{
|
{
|
int j = i + start;
|
int j = i + start;
|
|
|
/* Everything exept cause breakpoint must match. */
|
/* Everything exept cause breakpoint must match. */
|
if (dcr[j].dp != match[i].dcr.dp
|
if (dcr[j].dp != match[i].dcr.dp
|
|| dcr[j].ct != match[i].dcr.ct
|
|| dcr[j].ct != match[i].dcr.ct
|
|| dcr[j].cc != match[i].dcr.cc
|
|| dcr[j].cc != match[i].dcr.cc
|
|| dcr[j].sc != match[i].dcr.sc
|
|| dcr[j].sc != match[i].dcr.sc
|
|| dvr[j] != match[i].dvr
|
|| dvr[j] != match[i].dvr
|
|| match[i].chain_type != (dmr1 >> (2 * j)) & 3)
|
|| match[i].chain_type != (dmr1 >> (2 * j)) & 3)
|
return 0;
|
return 0;
|
}
|
}
|
return 1;
|
return 1;
|
}
|
}
|
|
|
static void
|
static void
|
hwatch_command (arg, from_tty)
|
hwatch_command (arg, from_tty)
|
char *arg;
|
char *arg;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
struct expression *exp;
|
struct expression *exp;
|
int i, nfree, nmatch, remove = 0;
|
int i, nfree, nmatch, remove = 0;
|
struct matchpoint *match;
|
struct matchpoint *match;
|
|
|
if (arg == NULL)
|
if (arg == NULL)
|
arg = "";
|
arg = "";
|
if (strncasecmp ("remove ", arg, 7) == 0)
|
if (strncasecmp ("remove ", arg, 7) == 0)
|
{
|
{
|
arg += 7;
|
arg += 7;
|
remove = 1;
|
remove = 1;
|
}
|
}
|
|
|
/* Parse arguments. */
|
/* Parse arguments. */
|
exp = parse_exp_1 (&arg, 0, 0);
|
exp = parse_exp_1 (&arg, 0, 0);
|
|
|
#ifdef DEBUG
|
#ifdef DEBUG
|
dump_prefix_expression (exp, gdb_stdout, "expr1");
|
dump_prefix_expression (exp, gdb_stdout, "expr1");
|
#endif
|
#endif
|
|
|
if (or1k_parse_or (&exp->elts[0], &match, &nmatch, 1) == &exp_error)
|
if (or1k_parse_or (&exp->elts[0], &match, &nmatch, 1) == &exp_error)
|
error ("Watchpoint too complex.");
|
error ("Watchpoint too complex.");
|
|
|
for (i = 0; i < nmatch; i++)
|
for (i = 0; i < nmatch; i++)
|
print_matchpoint_struct (&match[i]);
|
print_matchpoint_struct (&match[i]);
|
|
|
if (remove)
|
if (remove)
|
{
|
{
|
int start = -1;
|
int start = -1;
|
int cleared = 0;
|
int cleared = 0;
|
|
|
if (num_hw_watches <= 0)
|
if (num_hw_watches <= 0)
|
error ("No extended hardware supported watchpoints present.");
|
error ("No extended hardware supported watchpoints present.");
|
|
|
for (i = 0; i < num_hw_watches; i++)
|
for (i = 0; i < num_hw_watches; i++)
|
if (matchpoint_matches (or1k_hwatch[i].matchpoint_start, match, nmatch))
|
if (matchpoint_matches (or1k_hwatch[i].matchpoint_start, match, nmatch))
|
{
|
{
|
start = or1k_hwatch[i].matchpoint_start;
|
start = or1k_hwatch[i].matchpoint_start;
|
break;
|
break;
|
}
|
}
|
|
|
if (start < 0)
|
if (start < 0)
|
error ("Watchpoint not found.");
|
error ("Watchpoint not found.");
|
|
|
for (i = 0; i < nmatch; i++)
|
for (i = 0; i < nmatch; i++)
|
{
|
{
|
int j = start + i;
|
int j = start + i;
|
if (--matchpoint_user_count[j] <= 0)
|
if (--matchpoint_user_count[j] <= 0)
|
{
|
{
|
debug_regs_changed = 1;
|
debug_regs_changed = 1;
|
memset (&dcr[j], 0, sizeof (dcr[j]));
|
memset (&dcr[j], 0, sizeof (dcr[j]));
|
or1k_implementation.num_used_matchpoints--;
|
or1k_implementation.num_used_matchpoints--;
|
cleared = 1;
|
cleared = 1;
|
}
|
}
|
}
|
}
|
if (!cleared)
|
if (!cleared)
|
warning ("No matchpoint(s) freed. Resources are busy.");
|
warning ("No matchpoint(s) freed. Resources are busy.");
|
}
|
}
|
else
|
else
|
{
|
{
|
if (num_hw_watches >= MAX_HW_WATCHES)
|
if (num_hw_watches >= MAX_HW_WATCHES)
|
error ("Number of watchpoints too large.");
|
error ("Number of watchpoints too large.");
|
|
|
/* Now we have to find out if given prefix expression matches
|
/* Now we have to find out if given prefix expression matches
|
our HW based support. It may take up to
|
our HW based support. It may take up to
|
or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints. */
|
or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints. */
|
nfree = or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints;
|
nfree = or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints;
|
|
|
if (nmatch > nfree)
|
if (nmatch > nfree)
|
error ("Not enough free matchpoint resources.");
|
error ("Not enough free matchpoint resources.");
|
|
|
/* Build watchpoint(s) based on just built structure. */
|
/* Build watchpoint(s) based on just built structure. */
|
or1k_hwatch[num_hw_watches].matchpoint_start = or1k_implementation.num_used_matchpoints;
|
or1k_hwatch[num_hw_watches].matchpoint_start = or1k_implementation.num_used_matchpoints;
|
set_matchpoints (match, nmatch);
|
set_matchpoints (match, nmatch);
|
num_hw_watches++;
|
num_hw_watches++;
|
printf_unfiltered ("Watchpoint successfully allocated.\n");
|
printf_unfiltered ("Watchpoint successfully allocated.\n");
|
}
|
}
|
free (match);
|
free (match);
|
free (exp);
|
free (exp);
|
}
|
}
|
|
|
static void
|
static void
|
htrace_command (args, from_tty)
|
htrace_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
help_list (htrace_cmdlist, "htrace ", all_commands, gdb_stdout);
|
help_list (htrace_cmdlist, "htrace ", all_commands, gdb_stdout);
|
}
|
}
|
|
|
static void
|
static void
|
htrace_mode_command (args, from_tty)
|
htrace_mode_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
help_list (htrace_mode_cmdlist, "htrace mode ", all_commands, gdb_stdout);
|
help_list (htrace_mode_cmdlist, "htrace mode ", all_commands, gdb_stdout);
|
}
|
}
|
|
|
static void
|
static void
|
htrace_mode_contin_command (args, from_tty)
|
htrace_mode_contin_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
or1k_htrace.moder.contin = 1;
|
or1k_htrace.moder.contin = 1;
|
printf_unfiltered ("Continuous trace mode set.\n");
|
printf_unfiltered ("Continuous trace mode set.\n");
|
}
|
}
|
|
|
static void
|
static void
|
htrace_mode_suspend_command (args, from_tty)
|
htrace_mode_suspend_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
or1k_htrace.moder.contin = 0;
|
or1k_htrace.moder.contin = 0;
|
printf_unfiltered ("Suspend trace mode set.\n");
|
printf_unfiltered ("Suspend trace mode set.\n");
|
}
|
}
|
|
|
static void print_event_struct (event, stop)
|
static void print_event_struct (event, stop)
|
struct htrace_event_struct *event;
|
struct htrace_event_struct *event;
|
int stop;
|
int stop;
|
{
|
{
|
int i;
|
int i;
|
if (event->operation == TRIGOP_ANY)
|
if (event->operation == TRIGOP_ANY)
|
if (stop)
|
if (stop)
|
printf_filtered ("not active");
|
printf_filtered ("not active");
|
else
|
else
|
printf_filtered ("always active");
|
printf_filtered ("always active");
|
else
|
else
|
{
|
{
|
char *comma;
|
char *comma;
|
if (event->operation == TRIGOP_AND)
|
if (event->operation == TRIGOP_AND)
|
comma = "&(";
|
comma = "&(";
|
else
|
else
|
comma = "|(";
|
comma = "|(";
|
|
|
if (event->is_valid)
|
if (event->is_valid)
|
{
|
{
|
printf_filtered ("%s%s", comma, or1k_is_names[event->is_trig]);
|
printf_filtered ("%s%s", comma, or1k_is_names[event->is_trig]);
|
comma = ", ";
|
comma = ", ";
|
}
|
}
|
if (event->ls_valid)
|
if (event->ls_valid)
|
{
|
{
|
printf_filtered ("%s%s", comma, or1k_ls_names[event->ls_trig]);
|
printf_filtered ("%s%s", comma, or1k_ls_names[event->ls_trig]);
|
comma = ", ";
|
comma = ", ";
|
}
|
}
|
if (event->bp_valid)
|
if (event->bp_valid)
|
{
|
{
|
printf_filtered ("%sbreak", comma);
|
printf_filtered ("%sbreak", comma);
|
comma = ", ";
|
comma = ", ";
|
}
|
}
|
if (event->wp_valid)
|
if (event->wp_valid)
|
for (i = 0; i < 11; i++)
|
for (i = 0; i < 11; i++)
|
if ((event->wp_trig >> i) & 1)
|
if ((event->wp_trig >> i) & 1)
|
{
|
{
|
printf_filtered ("%sWP%i", comma, i);
|
printf_filtered ("%sWP%i", comma, i);
|
comma = ", ";
|
comma = ", ";
|
}
|
}
|
if (comma[0] == ',')
|
if (comma[0] == ',')
|
printf_filtered (")");
|
printf_filtered (")");
|
else
|
else
|
printf_filtered ("not active");
|
printf_filtered ("not active");
|
}
|
}
|
}
|
}
|
|
|
static void
|
static void
|
print_record_struct (record)
|
print_record_struct (record)
|
struct htrace_record_struct *record;
|
struct htrace_record_struct *record;
|
{
|
{
|
int i;
|
int i;
|
char *comma = "";
|
char *comma = "";
|
for (i = 0; i < MAX_RECORD_NAMES; i++)
|
for (i = 0; i < MAX_RECORD_NAMES; i++)
|
{
|
{
|
if ((record->rec >> i)&1)
|
if ((record->rec >> i)&1)
|
{
|
{
|
printf_filtered ("%s%s", comma, or1k_record_names[i]);
|
printf_filtered ("%s%s", comma, or1k_record_names[i]);
|
comma = ", ";
|
comma = ", ";
|
}
|
}
|
}
|
}
|
if (!*comma)
|
if (!*comma)
|
printf_unfiltered ("none");
|
printf_unfiltered ("none");
|
}
|
}
|
|
|
static void
|
static void
|
htrace_info_command (args, from_tty)
|
htrace_info_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
int i;
|
int i;
|
printf_filtered ("Trace trigger: ");
|
printf_filtered ("Trace trigger: ");
|
print_event_struct (&or1k_htrace.trig, 0);
|
print_event_struct (&or1k_htrace.trig, 0);
|
printf_filtered ("\nTrace qualifier: ");
|
printf_filtered ("\nTrace qualifier: ");
|
print_event_struct (&or1k_htrace.qual, 0);
|
print_event_struct (&or1k_htrace.qual, 0);
|
for (i = 0; i < MAX_MATCHPOINTS; i++)
|
for (i = 0; i < MAX_MATCHPOINTS; i++)
|
{
|
{
|
printf_filtered ("\n WP%i records: ", i);
|
printf_filtered ("\n WP%i records: ", i);
|
print_record_struct (&or1k_htrace.recwp[i]);
|
print_record_struct (&or1k_htrace.recwp[i]);
|
}
|
}
|
printf_filtered ("\n BP records: ");
|
printf_filtered ("\n BP records: ");
|
print_record_struct (&or1k_htrace.recbp);
|
print_record_struct (&or1k_htrace.recbp);
|
printf_filtered ("\nTrace stop: ");
|
printf_filtered ("\nTrace stop: ");
|
print_event_struct (&or1k_htrace.stop, 1);
|
print_event_struct (&or1k_htrace.stop, 1);
|
printf_filtered ("\n");
|
printf_filtered ("\n");
|
}
|
}
|
|
|
/* Parses event from given string.
|
/* Parses event from given string.
|
Result is placed into event structure, and previously allocated
|
Result is placed into event structure, and previously allocated
|
resources are freed. Parameter stop is nonzero, when we are parsing
|
resources are freed. Parameter stop is nonzero, when we are parsing
|
for stop criteria. */
|
for stop criteria. */
|
|
|
static void
|
static void
|
parse_event (args, event, stop)
|
parse_event (args, event, stop)
|
char *args;
|
char *args;
|
struct htrace_event_struct *event;
|
struct htrace_event_struct *event;
|
int stop;
|
int stop;
|
{
|
{
|
int i, op_type = 0, was_last_op = 1, any = 0;
|
int i, op_type = 0, was_last_op = 1, any = 0;
|
|
|
/* Release previous resources. */
|
/* Release previous resources. */
|
for (i = 0; i < MAX_MATCHPOINTS; i++)
|
for (i = 0; i < MAX_MATCHPOINTS; i++)
|
{
|
{
|
if ((event->wp_trig << i) & 1)
|
if ((event->wp_trig << i) & 1)
|
if (--matchpoint_user_count[i] <= 0)
|
if (--matchpoint_user_count[i] <= 0)
|
{
|
{
|
memset (&dcr[i], 0, sizeof (dcr[i]));
|
memset (&dcr[i], 0, sizeof (dcr[i]));
|
debug_regs_changed = 1;
|
debug_regs_changed = 1;
|
or1k_implementation.num_used_matchpoints--;
|
or1k_implementation.num_used_matchpoints--;
|
}
|
}
|
}
|
}
|
|
|
event->is_valid = event->is_trig = 0;
|
event->is_valid = event->is_trig = 0;
|
event->ls_valid = event->ls_trig = 0;
|
event->ls_valid = event->ls_trig = 0;
|
event->bp_valid = event->bp_trig = 0;
|
event->bp_valid = event->bp_trig = 0;
|
event->wp_valid = event->wp_trig = 0;
|
event->wp_valid = event->wp_trig = 0;
|
|
|
if (args == NULL)
|
if (args == NULL)
|
args = "";
|
args = "";
|
while (*args == ' ')
|
while (*args == ' ')
|
args++;
|
args++;
|
|
|
while (*args != '\0')
|
while (*args != '\0')
|
{
|
{
|
if (strncasecmp ("breakpoint", args, 10) == 0)
|
if (strncasecmp ("breakpoint", args, 10) == 0)
|
{
|
{
|
if (!was_last_op)
|
if (!was_last_op)
|
error ("Syntax error.");
|
error ("Syntax error.");
|
was_last_op = 0;
|
was_last_op = 0;
|
event->bp_valid = event->bp_trig = 1;
|
event->bp_valid = event->bp_trig = 1;
|
}
|
}
|
else if (!stop && strncasecmp ("any", args, 3) == 0
|
else if (!stop && strncasecmp ("any", args, 3) == 0
|
|| stop && strncasecmp ("none", args, 4) == 0)
|
|| stop && strncasecmp ("none", args, 4) == 0)
|
{
|
{
|
if (!was_last_op)
|
if (!was_last_op)
|
error ("Syntax error.");
|
error ("Syntax error.");
|
was_last_op = 0;
|
was_last_op = 0;
|
any = 1;
|
any = 1;
|
}
|
}
|
else if (strncasecmp ("||", args, 2) == 0)
|
else if (strncasecmp ("||", args, 2) == 0)
|
{
|
{
|
if (op_type == TRIGOP_AND)
|
if (op_type == TRIGOP_AND)
|
error ("Only one type of logical operator allowed at a time.");
|
error ("Only one type of logical operator allowed at a time.");
|
op_type = TRIGOP_OR;
|
op_type = TRIGOP_OR;
|
if (was_last_op)
|
if (was_last_op)
|
error ("Syntax error.");
|
error ("Syntax error.");
|
was_last_op = 1;
|
was_last_op = 1;
|
args += 2;
|
args += 2;
|
}
|
}
|
else if (strncasecmp ("&&", args, 2) == 0)
|
else if (strncasecmp ("&&", args, 2) == 0)
|
{
|
{
|
if (op_type == TRIGOP_OR)
|
if (op_type == TRIGOP_OR)
|
error ("Only one type of logical operator allowed at a time.");
|
error ("Only one type of logical operator allowed at a time.");
|
op_type = TRIGOP_AND;
|
op_type = TRIGOP_AND;
|
if (was_last_op)
|
if (was_last_op)
|
error ("Syntax error.");
|
error ("Syntax error.");
|
was_last_op = 1;
|
was_last_op = 1;
|
args += 2;
|
args += 2;
|
}
|
}
|
else
|
else
|
{
|
{
|
int found = 0;
|
int found = 0;
|
if (!was_last_op)
|
if (!was_last_op)
|
error ("Syntax error.");
|
error ("Syntax error.");
|
was_last_op = 0;
|
was_last_op = 0;
|
|
|
/* Search through is and ls tables for a match. */
|
/* Search through is and ls tables for a match. */
|
for (i = 0; i < MAX_IS_NAMES; i++)
|
for (i = 0; i < MAX_IS_NAMES; i++)
|
if (strncasecmp (args, or1k_is_names[i], strlen (or1k_is_names[i])) == 0)
|
if (strncasecmp (args, or1k_is_names[i], strlen (or1k_is_names[i])) == 0)
|
{
|
{
|
event->is_valid = 1;
|
event->is_valid = 1;
|
event->is_trig = i;
|
event->is_trig = i;
|
args += strlen (or1k_is_names[i]);
|
args += strlen (or1k_is_names[i]);
|
found = 1;
|
found = 1;
|
break;
|
break;
|
}
|
}
|
if (!found)
|
if (!found)
|
{
|
{
|
for (i = 0; i < MAX_LS_NAMES; i++)
|
for (i = 0; i < MAX_LS_NAMES; i++)
|
if (strncasecmp (args, or1k_ls_names[i], strlen (or1k_ls_names[i])) == 0)
|
if (strncasecmp (args, or1k_ls_names[i], strlen (or1k_ls_names[i])) == 0)
|
{
|
{
|
event->ls_valid = 1;
|
event->ls_valid = 1;
|
event->ls_trig = i;
|
event->ls_trig = i;
|
args += strlen (or1k_ls_names[i]);
|
args += strlen (or1k_ls_names[i]);
|
found = 1;
|
found = 1;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
if (!found)
|
if (!found)
|
{
|
{
|
/* No special name was found => parse expression. */
|
/* No special name was found => parse expression. */
|
struct expression *exp;
|
struct expression *exp;
|
struct matchpoint *match;
|
struct matchpoint *match;
|
int nmatch, nfree;
|
int nmatch, nfree;
|
|
|
exp = parse_exp_1 (&args, 0, 0);
|
exp = parse_exp_1 (&args, 0, 0);
|
|
|
if (or1k_parse_any (&exp->elts[0], &match, &nmatch, 3) == &exp_error)
|
if (or1k_parse_any (&exp->elts[0], &match, &nmatch, 3) == &exp_error)
|
error ("Expression too complex.");
|
error ("Expression too complex.");
|
for (i = 0; i < nmatch; i++)
|
for (i = 0; i < nmatch; i++)
|
print_matchpoint_struct (&match[i]);
|
print_matchpoint_struct (&match[i]);
|
|
|
/* Now we have to find out if given prefix expression matches
|
/* Now we have to find out if given prefix expression matches
|
our HW based support. It may take up to
|
our HW based support. It may take up to
|
or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints. */
|
or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints. */
|
nfree = or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints;
|
nfree = or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints;
|
|
|
if (nmatch > nfree)
|
if (nmatch > nfree)
|
error ("Not enough free matchpoint resources.");
|
error ("Not enough free matchpoint resources.");
|
|
|
/* Build matchpoint(s) based on just built structure. */
|
/* Build matchpoint(s) based on just built structure. */
|
set_matchpoints (match, nmatch);
|
set_matchpoints (match, nmatch);
|
event->wp_valid = 1;
|
event->wp_valid = 1;
|
event->wp_trig |= 1 << (or1k_implementation.num_used_matchpoints - 1);
|
event->wp_trig |= 1 << (or1k_implementation.num_used_matchpoints - 1);
|
printf_unfiltered ("Watchpoint successfully allocated.\n");
|
printf_unfiltered ("Watchpoint successfully allocated.\n");
|
free (match);
|
free (match);
|
free (exp);
|
free (exp);
|
found = 1;
|
found = 1;
|
}
|
}
|
if (!found)
|
if (!found)
|
warning ("Invalid event at '%s'", args);
|
warning ("Invalid event at '%s'", args);
|
}
|
}
|
while (*args == ' ') args++;
|
while (*args == ' ') args++;
|
}
|
}
|
if (any)
|
if (any)
|
event->operation = TRIGOP_ANY;
|
event->operation = TRIGOP_ANY;
|
else
|
else
|
{
|
{
|
if (op_type == 0)
|
if (op_type == 0)
|
op_type = TRIGOP_AND;
|
op_type = TRIGOP_AND;
|
event->operation = op_type;
|
event->operation = op_type;
|
}
|
}
|
}
|
}
|
|
|
static void
|
static void
|
htrace_trigger_command (args, from_tty)
|
htrace_trigger_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
parse_event (args, &or1k_htrace.trig, 0);
|
parse_event (args, &or1k_htrace.trig, 0);
|
printf_filtered ("Trace starts, when:\n");
|
printf_filtered ("Trace starts, when:\n");
|
print_event_struct (&or1k_htrace.trig, 0);
|
print_event_struct (&or1k_htrace.trig, 0);
|
printf_filtered ("\n");
|
printf_filtered ("\n");
|
}
|
}
|
|
|
static void
|
static void
|
htrace_qualifier_command (args, from_tty)
|
htrace_qualifier_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
parse_event (args, &or1k_htrace.qual, 0);
|
parse_event (args, &or1k_htrace.qual, 0);
|
printf_filtered ("Trace records, when:\n");
|
printf_filtered ("Trace records, when:\n");
|
print_event_struct (&or1k_htrace.qual, 0);
|
print_event_struct (&or1k_htrace.qual, 0);
|
printf_filtered ("\n");
|
printf_filtered ("\n");
|
}
|
}
|
|
|
static void
|
static void
|
htrace_stop_command (args, from_tty)
|
htrace_stop_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
parse_event (args, &or1k_htrace.stop, 1);
|
parse_event (args, &or1k_htrace.stop, 1);
|
printf_filtered ("Trace stops, when:\n");
|
printf_filtered ("Trace stops, when:\n");
|
print_event_struct (&or1k_htrace.stop, 1);
|
print_event_struct (&or1k_htrace.stop, 1);
|
printf_filtered ("\n");
|
printf_filtered ("\n");
|
}
|
}
|
|
|
static void
|
static void
|
htrace_clear_records_command (args, from_tty)
|
htrace_clear_records_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
int i, j, cleared = 0;
|
int i, j, cleared = 0;
|
|
|
/* Clear all. */
|
/* Clear all. */
|
for (i = 0; i < MAX_MATCHPOINTS; i++)
|
for (i = 0; i < MAX_MATCHPOINTS; i++)
|
{
|
{
|
for (j = 0; j < MAX_MATCHPOINTS; j++)
|
for (j = 0; j < MAX_MATCHPOINTS; j++)
|
{
|
{
|
if ((or1k_htrace.wp_record_uses[i] << j) & 1)
|
if ((or1k_htrace.wp_record_uses[i] << j) & 1)
|
if (--matchpoint_user_count[j] <= 0)
|
if (--matchpoint_user_count[j] <= 0)
|
{
|
{
|
memset (&dcr[j], 0, sizeof (dcr[j]));
|
memset (&dcr[j], 0, sizeof (dcr[j]));
|
debug_regs_changed = 1;
|
debug_regs_changed = 1;
|
cleared = 1;
|
cleared = 1;
|
or1k_implementation.num_used_matchpoints--;
|
or1k_implementation.num_used_matchpoints--;
|
}
|
}
|
}
|
}
|
or1k_htrace.wp_record_uses[i] = 0;
|
or1k_htrace.wp_record_uses[i] = 0;
|
}
|
}
|
if (!cleared)
|
if (!cleared)
|
warning ("No matchpoints freed. Resources are busy.");
|
warning ("No matchpoints freed. Resources are busy.");
|
}
|
}
|
|
|
/* Syntax: htrace record {data}* when {expr} */
|
/* Syntax: htrace record {data}* when {expr} */
|
|
|
static void
|
static void
|
htrace_record_command (args, from_tty)
|
htrace_record_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
struct expression *exp;
|
struct expression *exp;
|
int i, nfree, nmatch, wp;
|
int i, nfree, nmatch, wp;
|
struct matchpoint *match;
|
struct matchpoint *match;
|
unsigned int recdata = 0;
|
unsigned int recdata = 0;
|
char *c;
|
char *c;
|
|
|
if (args == '\0')
|
if (args == '\0')
|
error ( "Please specify data to record, e.g.:\n"
|
error ( "Please specify data to record, e.g.:\n"
|
"htrace record PC SDATA when $SEA == 100\n"
|
"htrace record PC SDATA when $SEA == 100\n"
|
"htrace record when $SEA == 100 to remove record");
|
"htrace record when $SEA == 100 to remove record");
|
|
|
for (i = 0; *args != '\0' && strncasecmp ("when ", args, 5); i++)
|
for (i = 0; *args != '\0' && strncasecmp ("when ", args, 5); i++)
|
{
|
{
|
int j, found = 0;
|
int j, found = 0;
|
for (j = 0; j < MAX_RECORD_NAMES; j++)
|
for (j = 0; j < MAX_RECORD_NAMES; j++)
|
if (strncasecmp (args, or1k_record_names[j], strlen (or1k_record_names[j])) == 0)
|
if (strncasecmp (args, or1k_record_names[j], strlen (or1k_record_names[j])) == 0)
|
{
|
{
|
recdata |= 1 << j;
|
recdata |= 1 << j;
|
found = 1;
|
found = 1;
|
break;
|
break;
|
}
|
}
|
if (!found)
|
if (!found)
|
warning ("Invalid record data name at '%s'.", args);
|
warning ("Invalid record data name at '%s'.", args);
|
while (*args != ' ' && *args != '\0') args++;
|
while (*args != ' ' && *args != '\0') args++;
|
while (*args == ' ') args++;
|
while (*args == ' ') args++;
|
}
|
}
|
|
|
if (strncasecmp ("when ", args, 5) != 0)
|
if (strncasecmp ("when ", args, 5) != 0)
|
if (*args == '\0')
|
if (*args == '\0')
|
{
|
{
|
warning ("Condition not set. Assuming breakpoint.");
|
warning ("Condition not set. Assuming breakpoint.");
|
wp = -1;
|
wp = -1;
|
}
|
}
|
else
|
else
|
error ("Syntax error.");
|
error ("Syntax error.");
|
else
|
else
|
{
|
{
|
args += 5;
|
args += 5;
|
if (strcasecmp ("breakpoint", args) == 0)
|
if (strcasecmp ("breakpoint", args) == 0)
|
wp = -1;
|
wp = -1;
|
else
|
else
|
{
|
{
|
/* Parse arguments. */
|
/* Parse arguments. */
|
exp = parse_exp_1 (&args, 0, 0);
|
exp = parse_exp_1 (&args, 0, 0);
|
|
|
#ifdef DEBUG
|
#ifdef DEBUG
|
dump_prefix_expression (exp, gdb_stdout, "expr1");
|
dump_prefix_expression (exp, gdb_stdout, "expr1");
|
#endif
|
#endif
|
if (or1k_parse_any (&exp->elts[0], &match, &nmatch, 3) == &exp_error)
|
if (or1k_parse_any (&exp->elts[0], &match, &nmatch, 3) == &exp_error)
|
error ("Expression too complex.");
|
error ("Expression too complex.");
|
|
|
for (i = 0; i < nmatch; i++)
|
for (i = 0; i < nmatch; i++)
|
print_matchpoint_struct (&match[i]);
|
print_matchpoint_struct (&match[i]);
|
|
|
if (recdata)
|
if (recdata)
|
{
|
{
|
/* Now we have to find out if given prefix expression matches
|
/* Now we have to find out if given prefix expression matches
|
our HW based support. It may take up to
|
our HW based support. It may take up to
|
or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints. */
|
or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints. */
|
nfree = or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints;
|
nfree = or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints;
|
|
|
if (nmatch > nfree)
|
if (nmatch > nfree)
|
error ("Not enough free matchpoint resources.");
|
error ("Not enough free matchpoint resources.");
|
|
|
wp = or1k_implementation.num_used_matchpoints - 1;
|
wp = or1k_implementation.num_used_matchpoints - 1;
|
or1k_htrace.wp_record_uses[wp] = 0;
|
or1k_htrace.wp_record_uses[wp] = 0;
|
for (i = or1k_implementation.num_used_matchpoints; i <= wp; i++)
|
for (i = or1k_implementation.num_used_matchpoints; i <= wp; i++)
|
or1k_htrace.wp_record_uses[wp] |= 1 << i;
|
or1k_htrace.wp_record_uses[wp] |= 1 << i;
|
set_matchpoints (match, nmatch);
|
set_matchpoints (match, nmatch);
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Remove record. */
|
/* Remove record. */
|
int start = -1, cleared = 0;
|
int start = -1, cleared = 0;
|
|
|
for (i = 0; i < MAX_MATCHPOINTS; i++)
|
for (i = 0; i < MAX_MATCHPOINTS; i++)
|
{
|
{
|
int mp_start = 0, j;
|
int mp_start = 0, j;
|
j = or1k_htrace.wp_record_uses[i];
|
j = or1k_htrace.wp_record_uses[i];
|
while (j > 0 && j & 1 == 0)
|
while (j > 0 && j & 1 == 0)
|
mp_start++;
|
mp_start++;
|
|
|
if (matchpoint_matches (mp_start, match, nmatch))
|
if (matchpoint_matches (mp_start, match, nmatch))
|
{
|
{
|
start = mp_start;
|
start = mp_start;
|
or1k_htrace.wp_record_uses[i] = 0;
|
or1k_htrace.wp_record_uses[i] = 0;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
if (start < 0)
|
if (start < 0)
|
error ("Record with such expression not found.");
|
error ("Record with such expression not found.");
|
|
|
for (i = 0; i < nmatch; i++)
|
for (i = 0; i < nmatch; i++)
|
{
|
{
|
int j = i + start;
|
int j = i + start;
|
if (--matchpoint_user_count[j] <= 0)
|
if (--matchpoint_user_count[j] <= 0)
|
{
|
{
|
memset (&dcr[j], 0, sizeof (dcr[j]));
|
memset (&dcr[j], 0, sizeof (dcr[j]));
|
debug_regs_changed = 1;
|
debug_regs_changed = 1;
|
cleared = 1;
|
cleared = 1;
|
}
|
}
|
}
|
}
|
if (!cleared)
|
if (!cleared)
|
warning ("No matchpoint(s) freed.");
|
warning ("No matchpoint(s) freed.");
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* If we reached this point we have matchpoints set, and wp
|
/* If we reached this point we have matchpoints set, and wp
|
holds the value of that watchpoint. wp == -1, if breakpoint
|
holds the value of that watchpoint. wp == -1, if breakpoint
|
was specified. */
|
was specified. */
|
if (wp < 0)
|
if (wp < 0)
|
or1k_htrace.recbp.rec = recdata;
|
or1k_htrace.recbp.rec = recdata;
|
else
|
else
|
or1k_htrace.recwp[wp].rec = recdata;
|
or1k_htrace.recwp[wp].rec = recdata;
|
|
|
if (recdata)
|
if (recdata)
|
{
|
{
|
printf_unfiltered ("Data");
|
printf_unfiltered ("Data");
|
for (i = 0; i < MAX_RECORD_NAMES; i++)
|
for (i = 0; i < MAX_RECORD_NAMES; i++)
|
if ((recdata >> i) & 1)
|
if ((recdata >> i) & 1)
|
printf_unfiltered (" %s,", or1k_record_names[i]);
|
printf_unfiltered (" %s,", or1k_record_names[i]);
|
}
|
}
|
else
|
else
|
printf_unfiltered ("No data");
|
printf_unfiltered ("No data");
|
if (wp < 0)
|
if (wp < 0)
|
printf_unfiltered (" will be recorded when breakpoint occurs\n");
|
printf_unfiltered (" will be recorded when breakpoint occurs\n");
|
else
|
else
|
printf_unfiltered (" will be recorded when watchpoint #%i occurs\n", wp);
|
printf_unfiltered (" will be recorded when watchpoint #%i occurs\n", wp);
|
}
|
}
|
|
|
static void
|
static void
|
htrace_enable_command (args, from_tty)
|
htrace_enable_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
or1k_htrace.moder.trace_enable = 1;
|
or1k_htrace.moder.trace_enable = 1;
|
printf_unfiltered ("HW Trace enabled.\n");
|
printf_unfiltered ("HW Trace enabled.\n");
|
}
|
}
|
|
|
static void
|
static void
|
htrace_disable_command (args, from_tty)
|
htrace_disable_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
or1k_htrace.moder.trace_enable = 0;
|
or1k_htrace.moder.trace_enable = 0;
|
printf_unfiltered ("HW Trace disabled.\n");
|
printf_unfiltered ("HW Trace disabled.\n");
|
}
|
}
|
|
|
static void
|
static void
|
htrace_rewind_command (args, from_tty)
|
htrace_rewind_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
FILE *f;
|
FILE *f;
|
if (args != NULL && *args != '\0')
|
if (args != NULL && *args != '\0')
|
strncpy (TRACE_FILENAME, args, TRACE_FILENAME_SIZE);
|
strncpy (TRACE_FILENAME, args, TRACE_FILENAME_SIZE);
|
|
|
/* Just empty it. */
|
/* Just empty it. */
|
if ((f = fopen (TRACE_FILENAME, "wb+")) == NULL)
|
if ((f = fopen (TRACE_FILENAME, "wb+")) == NULL)
|
error ("Cannot open trace file.");
|
error ("Cannot open trace file.");
|
fclose (f);
|
fclose (f);
|
printf_unfiltered ("Trace data cleared.\n");
|
printf_unfiltered ("Trace data cleared.\n");
|
}
|
}
|
|
|
static void
|
static void
|
print_data_struct (pos, data)
|
print_data_struct (pos, data)
|
unsigned int pos;
|
unsigned int pos;
|
struct htrace_data_struct *data;
|
struct htrace_data_struct *data;
|
{
|
{
|
struct symbol *func;
|
struct symbol *func;
|
char *funname = NULL;
|
char *funname = NULL;
|
|
|
if (data->type < 4)
|
if (data->type < 4)
|
{
|
{
|
/* Determine function name - copied from stack.c */
|
/* Determine function name - copied from stack.c */
|
func = find_pc_function (data->data);
|
func = find_pc_function (data->data);
|
if (func)
|
if (func)
|
{
|
{
|
struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (data->data);
|
struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (data->data);
|
if (msymbol != NULL
|
if (msymbol != NULL
|
&& (SYMBOL_VALUE_ADDRESS (msymbol)
|
&& (SYMBOL_VALUE_ADDRESS (msymbol)
|
> BLOCK_START (SYMBOL_BLOCK_VALUE (func))))
|
> BLOCK_START (SYMBOL_BLOCK_VALUE (func))))
|
funname = SYMBOL_NAME (msymbol);
|
funname = SYMBOL_NAME (msymbol);
|
else
|
else
|
{
|
{
|
char *demangled;
|
char *demangled;
|
funname = SYMBOL_NAME (func);
|
funname = SYMBOL_NAME (func);
|
if (SYMBOL_LANGUAGE (func) == language_cplus)
|
if (SYMBOL_LANGUAGE (func) == language_cplus)
|
{
|
{
|
demangled = cplus_demangle (funname, DMGL_ANSI);
|
demangled = cplus_demangle (funname, DMGL_ANSI);
|
if (demangled == NULL)
|
if (demangled == NULL)
|
funname = SYMBOL_SOURCE_NAME (func);
|
funname = SYMBOL_SOURCE_NAME (func);
|
}
|
}
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (data->data);
|
struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (data->data);
|
if (msymbol != NULL)
|
if (msymbol != NULL)
|
funname = SYMBOL_NAME (msymbol);
|
funname = SYMBOL_NAME (msymbol);
|
}
|
}
|
}
|
}
|
|
|
printf_filtered ("%06X%c %-8s %08X", pos, data->valid ? '>' : ':',
|
printf_filtered ("%06X%c %-8s %08X", pos, data->valid ? '>' : ':',
|
or1k_record_names[data->type], data->data);
|
or1k_record_names[data->type], data->data);
|
if (funname)
|
if (funname)
|
printf_filtered (" (%s)\n", funname);
|
printf_filtered (" (%s)\n", funname);
|
else
|
else
|
printf_filtered ("\n");
|
printf_filtered ("\n");
|
}
|
}
|
|
|
/* Prints out trace buffer. */
|
/* Prints out trace buffer. */
|
|
|
static void
|
static void
|
htrace_print_command (args, from_tty)
|
htrace_print_command (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
int i, from = 0, length = prev_length;
|
int i, from = 0, length = prev_length;
|
FILE *f;
|
FILE *f;
|
struct htrace_data_struct *td;
|
struct htrace_data_struct *td;
|
|
|
if (args == NULL) args = "";
|
if (args == NULL) args = "";
|
while (*args == ' ') args++;
|
while (*args == ' ') args++;
|
if (*args == '\0')
|
if (*args == '\0')
|
{
|
{
|
/* We will display buffer further. */
|
/* We will display buffer further. */
|
from = prev_from + prev_length;
|
from = prev_from + prev_length;
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Display buffer range. */
|
/* Display buffer range. */
|
int numbers = 0;
|
int numbers = 0;
|
char *cnum = args;
|
char *cnum = args;
|
while (*args != ' ' && *args != '\0')
|
while (*args != ' ' && *args != '\0')
|
args++;
|
args++;
|
|
|
/* Any arguments? */
|
/* Any arguments? */
|
if (*args == '\0')
|
if (*args == '\0')
|
numbers = 1;
|
numbers = 1;
|
else
|
else
|
{
|
{
|
*args = 0;
|
*args = 0;
|
args++;
|
args++;
|
numbers = 2;
|
numbers = 2;
|
}
|
}
|
from = strtoul (cnum, &cnum, 0);
|
from = strtoul (cnum, &cnum, 0);
|
if (*cnum != 0)
|
if (*cnum != 0)
|
error ("Invalid from value.");
|
error ("Invalid from value.");
|
if (from < 0) from += trace_size;
|
if (from < 0) from += trace_size;
|
if (numbers == 2)
|
if (numbers == 2)
|
{
|
{
|
while (*args == ' ') args++;
|
while (*args == ' ') args++;
|
length = strtoul (cnum, &cnum, 0);
|
length = strtoul (cnum, &cnum, 0);
|
if (*args != 0)
|
if (*args != 0)
|
error ("Invalid length value.");
|
error ("Invalid length value.");
|
if (length < 0)
|
if (length < 0)
|
{
|
{
|
from += length;
|
from += length;
|
length = -length;
|
length = -length;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
if (from >= trace_size)
|
if (from >= trace_size)
|
from = trace_size - 1;
|
from = trace_size - 1;
|
if (from < 0)
|
if (from < 0)
|
from = 0;
|
from = 0;
|
if (from + length >= trace_size)
|
if (from + length >= trace_size)
|
length = trace_size - from;
|
length = trace_size - from;
|
|
|
prev_length = length;
|
prev_length = length;
|
prev_from = from;
|
prev_from = from;
|
if (length == 0)
|
if (length == 0)
|
error ("Nothing to print.");
|
error ("Nothing to print.");
|
|
|
printf_filtered ("Trace buffer %06x:%06x (size = %i)\n", from, from + length - 1, length);
|
printf_filtered ("Trace buffer %06x:%06x (size = %i)\n", from, from + length - 1, length);
|
if ((f = fopen (TRACE_FILENAME, "rb")) == NULL)
|
if ((f = fopen (TRACE_FILENAME, "rb")) == NULL)
|
error ("Cannot open trace file.");
|
error ("Cannot open trace file.");
|
if (fseek (f, TRACE_DATA_SIZE * from, SEEK_SET))
|
if (fseek (f, TRACE_DATA_SIZE * from, SEEK_SET))
|
error ("Error reading trace file.");
|
error ("Error reading trace file.");
|
td = (struct htrace_data_struct *) malloc (TRACE_DATA_SIZE * length);
|
td = (struct htrace_data_struct *) malloc (TRACE_DATA_SIZE * length);
|
length = fread (td, TRACE_DATA_SIZE, length, f);
|
length = fread (td, TRACE_DATA_SIZE, length, f);
|
for (i = 0; i < length; i++)
|
for (i = 0; i < length; i++)
|
print_data_struct (from + i, &td[i]);
|
print_data_struct (from + i, &td[i]);
|
fclose (f);
|
fclose (f);
|
}
|
}
|
|
|
int print_insn_big_or32 (bfd_vma,struct disassemble_info*);
|
int print_insn_big_or32 (bfd_vma,struct disassemble_info*);
|
int print_insn_little_or32 (bfd_vma,struct disassemble_info*);
|
int print_insn_little_or32 (bfd_vma,struct disassemble_info*);
|
|
|
void
|
void
|
_initialize_or1k_tdep ()
|
_initialize_or1k_tdep ()
|
{
|
{
|
build_automata ();
|
build_automata ();
|
/* Added by CZ 26/06/01 */
|
/* Added by CZ 26/06/01 */
|
if(TARGET_BYTE_ORDER == BIG_ENDIAN)
|
if(TARGET_BYTE_ORDER == BIG_ENDIAN)
|
tm_print_insn = print_insn_big_or32;
|
tm_print_insn = print_insn_big_or32;
|
else
|
else
|
tm_print_insn = print_insn_little_or32;
|
tm_print_insn = print_insn_little_or32;
|
|
|
/* Commands to show and set sprs. */
|
/* Commands to show and set sprs. */
|
add_info ("spr", info_spr_command, "Show information about the spr registers.");
|
add_info ("spr", info_spr_command, "Show information about the spr registers.");
|
add_info ("trace", info_trace_command, "Display trace buffer contents.");
|
add_info ("trace", info_trace_command, "Display trace buffer contents.");
|
add_com ("spr", class_support, spr_command, "Set specified SPR register.");
|
add_com ("spr", class_support, spr_command, "Set specified SPR register.");
|
|
|
/* hwatch command. */
|
/* hwatch command. */
|
add_com ("hwatch", class_breakpoint, hwatch_command, "Set hardware watch"
|
add_com ("hwatch", class_breakpoint, hwatch_command, "Set hardware watch"
|
"point.\nExample: ($LEA == my_var)&&($LDATA < 50)||($SEA == my_"
|
"point.\nExample: ($LEA == my_var)&&($LDATA < 50)||($SEA == my_"
|
"var)&&($SDATA >= 50).\nSee OR1k Architecture document for more"
|
"var)&&($SDATA >= 50).\nSee OR1k Architecture document for more"
|
" info.");
|
" info.");
|
|
|
/* htrace commands. */
|
/* htrace commands. */
|
add_prefix_cmd ("htrace", class_breakpoint, htrace_command,
|
add_prefix_cmd ("htrace", class_breakpoint, htrace_command,
|
"Group of commands for handling hardware assisted trace\n\n"
|
"Group of commands for handling hardware assisted trace\n\n"
|
"See OR1k Architecture and gdb for or1k documents for more info.",
|
"See OR1k Architecture and gdb for or1k documents for more info.",
|
&htrace_cmdlist, "htrace ", 0, &cmdlist);
|
&htrace_cmdlist, "htrace ", 0, &cmdlist);
|
add_cmd ("info", class_breakpoint, htrace_info_command, "Display information about HW trace.",
|
add_cmd ("info", class_breakpoint, htrace_info_command, "Display information about HW trace.",
|
&htrace_cmdlist);
|
&htrace_cmdlist);
|
add_alias_cmd ("i", "info", class_breakpoint, 1, &htrace_cmdlist);
|
add_alias_cmd ("i", "info", class_breakpoint, 1, &htrace_cmdlist);
|
add_cmd ("trigger", class_breakpoint, htrace_trigger_command, "Set starting criteria for trace.",
|
add_cmd ("trigger", class_breakpoint, htrace_trigger_command, "Set starting criteria for trace.",
|
&htrace_cmdlist);
|
&htrace_cmdlist);
|
add_alias_cmd ("t", "trigger", class_breakpoint, 1, &htrace_cmdlist);
|
add_alias_cmd ("t", "trigger", class_breakpoint, 1, &htrace_cmdlist);
|
add_cmd ("qualifier", class_breakpoint, htrace_qualifier_command, "Set acquisition qualifier for HW trace.",
|
add_cmd ("qualifier", class_breakpoint, htrace_qualifier_command, "Set acquisition qualifier for HW trace.",
|
&htrace_cmdlist);
|
&htrace_cmdlist);
|
add_alias_cmd ("q", "qualifier", class_breakpoint, 1, &htrace_cmdlist);
|
add_alias_cmd ("q", "qualifier", class_breakpoint, 1, &htrace_cmdlist);
|
add_cmd ("stop", class_breakpoint, htrace_stop_command, "Set HW trace stopping criteria.",
|
add_cmd ("stop", class_breakpoint, htrace_stop_command, "Set HW trace stopping criteria.",
|
&htrace_cmdlist);
|
&htrace_cmdlist);
|
add_alias_cmd ("s", "stop", class_breakpoint, 1, &htrace_cmdlist);
|
add_alias_cmd ("s", "stop", class_breakpoint, 1, &htrace_cmdlist);
|
add_cmd ("record", class_breakpoint, htrace_record_command, "Sets data to be recorded when expression occurs.",
|
add_cmd ("record", class_breakpoint, htrace_record_command, "Sets data to be recorded when expression occurs.",
|
&htrace_cmdlist);
|
&htrace_cmdlist);
|
add_alias_cmd ("r", "record", class_breakpoint, 1, &htrace_cmdlist);
|
add_alias_cmd ("r", "record", class_breakpoint, 1, &htrace_cmdlist);
|
add_cmd ("clear records", class_breakpoint, htrace_clear_records_command,
|
add_cmd ("clear records", class_breakpoint, htrace_clear_records_command,
|
"Disposes all matchpoints used by records.", &htrace_cmdlist);
|
"Disposes all matchpoints used by records.", &htrace_cmdlist);
|
add_cmd ("enable", class_breakpoint, htrace_enable_command, "Enables the HW trace.", &htrace_cmdlist);
|
add_cmd ("enable", class_breakpoint, htrace_enable_command, "Enables the HW trace.", &htrace_cmdlist);
|
add_alias_cmd ("e", "enable", class_breakpoint, 1, &htrace_cmdlist);
|
add_alias_cmd ("e", "enable", class_breakpoint, 1, &htrace_cmdlist);
|
add_cmd ("disable", class_breakpoint, htrace_disable_command, "Disables the HW trace.", &htrace_cmdlist);
|
add_cmd ("disable", class_breakpoint, htrace_disable_command, "Disables the HW trace.", &htrace_cmdlist);
|
add_alias_cmd ("d", "disable", class_breakpoint, 1, &htrace_cmdlist);
|
add_alias_cmd ("d", "disable", class_breakpoint, 1, &htrace_cmdlist);
|
add_cmd ("rewind", class_breakpoint, htrace_rewind_command, "Clears currently recorded trace data.\n"
|
add_cmd ("rewind", class_breakpoint, htrace_rewind_command, "Clears currently recorded trace data.\n"
|
"If filename is specified, new trace file is made and any newly collected data\n"
|
"If filename is specified, new trace file is made and any newly collected data\n"
|
"will be written there.", &htrace_cmdlist);
|
"will be written there.", &htrace_cmdlist);
|
add_cmd ("print", class_breakpoint, htrace_print_command,
|
add_cmd ("print", class_breakpoint, htrace_print_command,
|
"Prints trace buffer, using current record configuration.\n"
|
"Prints trace buffer, using current record configuration.\n"
|
"htrace print [<start> [<len>]]\n"
|
"htrace print [<start> [<len>]]\n"
|
"htrace print"
|
"htrace print"
|
, &htrace_cmdlist);
|
, &htrace_cmdlist);
|
add_alias_cmd ("p", "print", class_breakpoint, 1, &htrace_cmdlist);
|
add_alias_cmd ("p", "print", class_breakpoint, 1, &htrace_cmdlist);
|
add_prefix_cmd ("mode", class_breakpoint, htrace_mode_command,
|
add_prefix_cmd ("mode", class_breakpoint, htrace_mode_command,
|
"Configures the HW trace.\n"
|
"Configures the HW trace.\n"
|
"htrace mode [continuous|suspend]"
|
"htrace mode [continuous|suspend]"
|
, &htrace_mode_cmdlist, "htrace mode ", 0, &htrace_cmdlist);
|
, &htrace_mode_cmdlist, "htrace mode ", 0, &htrace_cmdlist);
|
add_alias_cmd ("m", "mode", class_breakpoint, 1, &htrace_cmdlist);
|
add_alias_cmd ("m", "mode", class_breakpoint, 1, &htrace_cmdlist);
|
add_cmd ("continuous", class_breakpoint, htrace_mode_contin_command,
|
add_cmd ("continuous", class_breakpoint, htrace_mode_contin_command,
|
"Set continuous trace mode.\n", &htrace_mode_cmdlist);
|
"Set continuous trace mode.\n", &htrace_mode_cmdlist);
|
add_cmd ("suspend", class_breakpoint, htrace_mode_suspend_command,
|
add_cmd ("suspend", class_breakpoint, htrace_mode_suspend_command,
|
"Set suspend trace mode.\n", &htrace_mode_cmdlist);
|
"Set suspend trace mode.\n", &htrace_mode_cmdlist);
|
|
|
/* Extra functions supported by simulator. */
|
/* Extra functions supported by simulator. */
|
add_com ("sim", class_obscure, sim_command,
|
add_com ("sim", class_obscure, sim_command,
|
"Send a extended command to the simulator.");
|
"Send a extended command to the simulator.");
|
}
|
}
|
|
|
|
|