/*
Copyright (C) 1998-2003 T. Scott Dattalo
This file is part of gpsim.
gpsim is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
gpsim is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with gpsim; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/*
stuff that needs to be fixed:
Register aliasing
The "invalid instruction" in program memory.
*/
#include <stdio.h>
#ifdef _WIN32
#include "uxtime.h"
#else
#include <sys/time.h>
#endif
#include <time.h>
#include <iostream>
#include <iomanip>
#include <string>
#include "../config.h"
#include "gpsim_def.h"
#include <glib.h>
#include "gpsim_classes.h"
#include "modules.h"
#include "processor.h"
#include "xref.h"
#include "attributes.h"
#include "fopen-path.h"
#include "cmd_gpsim.h"
#include "sim_context.h"
//------------------------------------------------------------------------
// active_cpu is a pointer to the pic processor that is currently 'active'.
// 'active' means that it's the one currently being simulated or the one
// currently being manipulated by the user (e.g. register dumps, break settings)
Processor *active_cpu = 0;
// create instances of inline get_active_cpu() and set_active_cpu() methods
// by taking theirs address
Processor *(*dummy_get_active_cpu)(void) = get_active_cpu;
void (*dummy_set_active_cpu)(Processor *act_cpu) = set_active_cpu;
static char pkg_version[] = PACKAGE_VERSION;
//------------------------------------------------------------------------
//
// Processor - Constructor
//
Processor::Processor(const char *_name, const char *_desc)
: Module(_name, _desc)
{
registers = 0;
pma = 0;
m_pConstructorObject = 0;
m_Capabilities = 0;
if(verbose)
cout << "processor constructor\n";
pc = 0;
mFrequency = new Float("frequency",20e6, " oscillator frequency.");
set_ClockCycles_per_Instruction(4);
get_cycles().set_instruction_cps((guint64)(get_frequency()/clocks_per_inst));
set_Vdd(5.0);
setWarnMode(true);
setSafeMode(true);
setUnknownMode(true);
setBreakOnReset(true);
// derived classes need to override these values
m_uPageMask = 0x00;
m_uAddrMask = 0xff;
readTT = 0;
writeTT = 0;
interface = new ProcessorInterface(this);
// let the processor version number simply be gpsim's version number.
version = &pkg_version[0];
get_trace().cycle_counter(get_cycles().value);
}
//-------------------------------------------------------------------
Processor::~Processor()
{
// register_bank points to current active bank
// pc is allocated by the derived class
delete []program_memory;
delete registers;
destroyProgramMemoryAccess(pma);
}
unsigned long Processor::GetCapabilities() {
return m_Capabilities;
}
void Processor::initializeAttributes()
{
Module::initializeAttributes();
add_attribute(new WarnModeAttribute(this));
add_attribute(new SafeModeAttribute(this));
add_attribute(new UnknownModeAttribute(this));
add_attribute(new BreakOnResetAttribute(this));
m_pbBreakOnInvalidRegisterRead = new Boolean("BreakOnInvalidRegisterRead",
true, "Halt simulation when an invalid register is read from.");
m_pbBreakOnInvalidRegisterRead->setClearableSymbol(false);
add_attribute(m_pbBreakOnInvalidRegisterRead);
m_pbBreakOnInvalidRegisterWrite = new Boolean("BreakOnInvalidRegisterWrite",
true, "Halt simulation when an invalid register is written to.");
m_pbBreakOnInvalidRegisterWrite->setClearableSymbol(false);
add_attribute(m_pbBreakOnInvalidRegisterWrite);
add_attribute(mFrequency);
}
//-------------------------------------------------------------------
// Simulation modes:
void Processor::setWarnMode(bool newWarnMode)
{
bWarnMode = newWarnMode;
}
void Processor::setSafeMode(bool newSafeMode)
{
bSafeMode = newSafeMode;
}
void Processor::setUnknownMode(bool newUnknownMode)
{
bUnknownMode = newUnknownMode;
}
void Processor::setBreakOnReset(bool newBreakOnReset)
{
bBreakOnReset = newBreakOnReset;
}
//------------------------------------------------------------------------
// Attributes
void Processor::set_frequency(double f)
{
if(mFrequency)
mFrequency->set(f);
get_cycles().set_instruction_cps((guint64)(f/clocks_per_inst));
}
double Processor::get_frequency()
{
double d=0.0;
if(mFrequency)
mFrequency->get(d);
return d;
}
double Processor::get_OSCperiod()
{
double f = get_frequency();
if(f>0.0)
return 1/f;
else
return 0.0;
}
void Processor::set(const char *cP,int len)
{
}
void Processor::get(char *cP, int len)
{
cP[0] = 0;
}
//-------------------------------------------------------------------
//
// init_register_memory (unsigned int memory_size)
//
// Allocate an array for holding register objects.
//
void Processor::init_register_memory (unsigned int memory_size)
{
if(verbose)
cout << __FUNCTION__ << " memory size: " << memory_size << '\n';
registers = new Register *[memory_size];
m_UiAccessOfRegisters = new RegisterCollection(this,
"ramData",
registers,
memory_size);
if (registers == 0)
{
cout << "*** ERROR *** Out of memory - PIC register space\n";
exit (1);
}
// For processors with banked memory, the register_bank corresponds to the
// active bank. Let this point to the beginning of the register array for
// now.
register_bank = registers;
rma.set_cpu(this);
rma.set_Registers(registers, memory_size);
// Make all of the file registers 'undefined' (each processor derived from this base
// class defines its own register mapping).
for (unsigned int i = 0; i < memory_size; i++)
registers[i] = 0;
}
//-------------------------------------------------------------------
//
//
// create_invalid_registers
//
// The purpose of this function is to complete the initialization
// of the file register memory by placing an instance of an 'invalid
// file register' at each 'invalid' memory location. Most of PIC's
// do not use the entire address space available, so this routine
// fills the voids.
//
void Processor::create_invalid_registers ()
{
unsigned int addr;
if(verbose)
cout << "Creating invalid registers " << register_memory_size()<<"\n";
// Now, initialize any undefined register as an 'invalid register'
// Note, each invalid register is given its own object. This enables
// the simulation code to efficiently capture any invalid register
// access. Furthermore, it's possible to set break points on
// individual invalid file registers. By default, gpsim halts whenever
// there is an invalid file register access.
for (addr = 0; addr < register_memory_size(); addr+=map_rm_index2address(1)) {
unsigned int index = map_rm_address2index(addr);
if (0 == registers[index]) {
registers[index] = new InvalidRegister(addr);
registers[index]->alias_mask = 0;
registers[index]->set_cpu(this);
}
}
}
//-------------------------------------------------------------------
// add_file_registers
//
// The purpose of this member function is to allocate memory for the
// general purpose registers.
//
void Processor::add_file_registers(unsigned int start_address, unsigned int end_address, unsigned int alias_offset)
{
unsigned int j;
// Initialize the General Purpose Registers:
char str[100];
for (j = start_address; j <= end_address; j++) {
registers[j] = new Register;
if (alias_offset) {
registers[j + alias_offset] = registers[j];
registers[j]->alias_mask = alias_offset;
} else
registers[j]->alias_mask = 0;
registers[j]->address = j;
RegisterValue rv = getWriteTT(j);
registers[j]->set_write_trace(rv);
rv = getReadTT(j);
registers[j]->set_read_trace(rv);
//The default register name is simply its address
sprintf (str, "0x%02x", j);
registers[j]->new_name(str);
registers[j]->set_cpu(this);
}
}
//-------------------------------------------------------------------
// delete_file_registers
//
// The purpose of this member function is to delete file registers
//
void Processor::delete_file_registers(unsigned int start_address, unsigned int end_address)
{
// FIXME - this function is bogus.
// The aliased registers do not need to be searched for - the alias mask
// can tell at what addresses a register is aliased.
#define SMALLEST_ALIAS_DISTANCE 32
unsigned int i,j;
for (j = start_address; j <= end_address; j++) {
if(registers[j]) {
if(registers[j]->alias_mask) {
// This registers appears in more than one place. Let's find all
// of its aliases.
for(i=SMALLEST_ALIAS_DISTANCE; i<register_memory_size(); i+=SMALLEST_ALIAS_DISTANCE)
if(registers[j] == registers[i])
registers[i] = 0;
}
delete registers[j];
registers[j] = 0;
}
}
}
//-------------------------------------------------------------------
//
//
// alias_file_registers
//
// The purpose of this member function is to alias the
// general purpose registers.
//
void Processor::alias_file_registers(unsigned int start_address, unsigned int end_address, unsigned int alias_offset)
{
unsigned int j;
// FIXME -- it'd probably make better sense to keep a list of register addresses at
// which a particular register appears.
for (j = start_address; j <= end_address; j++)
{
if (alias_offset)
{
registers[j + alias_offset] = registers[j];
registers[j]->alias_mask = alias_offset;
}
}
}
//-------------------------------------------------------------------
//
// init_program_memory(unsigned int memory_size)
//
// The purpose of this member function is to allocate memory for the
// pic's code space. The 'memory_size' parameter tells how much memory
// is to be allocated AND it should be an integer of the form of 2^n.
// If the memory size is not of the form of 2^n, then this routine will
// round up to the next integer that is of the form 2^n.
// Once the memory has been allocated, this routine will initialize
// it with the 'bad_instruction'. The bad_instruction is an instantiation
// of the instruction class that chokes gpsim if it is executed. Note that
// there is only one instance of 'bad_instruction'.
void Processor::init_program_memory (unsigned int memory_size)
{
if(verbose)
cout << "Initializing program memory: 0x"<<memory_size<<" words\n";
if ((memory_size-1) & memory_size)
{
cout << "*** WARNING *** memory_size should be of the form 2^N\n";
memory_size = (memory_size + ~memory_size) & MAX_PROGRAM_MEMORY;
cout << "gpsim is rounding up to memory_size = " << memory_size << '\n';
}
// Initialize 'program_memory'. 'program_memory' is a pointer to an array of
// pointers of type 'instruction'. This is where the simulated instructions
// are stored.
program_memory = new instruction *[memory_size];
if (program_memory == 0)
{
cout << "*** ERROR *** Out of memory for program space\n";
exit (1);
}
for (unsigned int i = 0; i < memory_size; i++)
{
program_memory[i] = &bad_instruction;
program_memory[i]->set_cpu(this);
}
pma = createProgramMemoryAccess(this);
pma->name();
}
ProgramMemoryAccess * Processor::createProgramMemoryAccess(Processor *processor) {
return new ProgramMemoryAccess(processor);
}
void Processor::destroyProgramMemoryAccess(ProgramMemoryAccess *pma) {
delete pma;
}
//-------------------------------------------------------------------
// init_program_memory(int address, int value)
//
// The purpose of this member fucntion is to instantiate an Instruction
// object in the program memory. If the opcode is invalid, then a 'bad_instruction'
// is inserted into the program memory instead. If the address is beyond
// the program memory address space, then it may be that the 'opcode' is
// is in fact a configuration word.
//
void Processor::init_program_memory(unsigned int address, unsigned int value)
{
unsigned int uIndex = map_pm_address2index(address);
if (!program_memory) {
printf("ERROR: internal bug %s:%d",__FILE__,__LINE__);
exit(1);
}
if(uIndex < program_memory_size()) {
if(program_memory[uIndex] != 0 && program_memory[uIndex]->isa() != instruction::INVALID_INSTRUCTION) {
// this should not happen
delete program_memory[uIndex];
}
program_memory[uIndex] = disasm(address,value);
if(program_memory[uIndex] == 0)
program_memory[uIndex] = &bad_instruction;
program_memory[uIndex]->add_line_number_symbol(address);
}
else if (set_config_word(address, value))
;
else
set_out_of_range_pm(address,value); // could be e2prom
}
void Processor::init_program_memory_at_index(unsigned int uIndex, unsigned int value)
{
init_program_memory(map_pm_index2address(uIndex), value);
}
void Processor::init_program_memory_at_index(unsigned int uIndex,
const unsigned char *bytes, int nBytes)
{
for (int i =0; i<nBytes/2; i++)
init_program_memory_at_index(uIndex+i, (((unsigned int)bytes[2*i+1])<<8) | bytes[2*i]);
}
//------------------------------------------------------------------
// Fetch the rom contents at a particular address.
unsigned int Processor::get_program_memory_at_address(unsigned int address)
{
unsigned int uIndex = map_pm_address2index(address);
return (uIndex < program_memory_size() && program_memory[uIndex])
? program_memory[uIndex]->get_opcode()
: 0xffffffff;
}
//-------------------------------------------------------------------
// build_program_memory - given an array of opcodes this function
// will convert them into instructions and insert them into the
// simulated program memory.
//
void Processor::build_program_memory(unsigned int *memory,
unsigned int minaddr,
unsigned int maxaddr)
{
for (unsigned int i = minaddr; i <= maxaddr; i++)
if(memory[i] != 0xffffffff)
init_program_memory(i, memory[i]);
}
//-------------------------------------------------------------------
void Processor::set_out_of_range_pm(unsigned int address, unsigned int value)
{
cout << "Warning::Out of range address " << address << " value " << value << endl;
cout << "Max allowed address is 0x" << hex << (program_memory_size()-1) << '\n';
}
//-------------------------------------------------------------------
//
// attach_src_line - This member function establishes the one-to-one link
// between instructions and the source code that create them.
void Processor::attach_src_line(unsigned int address,
unsigned int file_id,
unsigned int sline,
unsigned int lst_line)
{
unsigned int uIndex = map_pm_address2index(address);
if(uIndex < program_memory_size())
program_memory[uIndex]->update_line_number(file_id,sline,lst_line,-1,-1);
}
//-------------------------------------------------------------------
// read_src_files - this routine will open all of the source files
// associated with the project and associate their line numbers
// with the addresses of the opcodes they generated.
//
void Processor::read_src_files(void)
{
int i;
// Are there any src files ?
for(i=0; i<files.nsrc_files(); i++) {
FileContext *fc = files[i];
// did this src file generate any code?
if(fc && fc->max_line() > 0) {
// Create an array whose index corresponds to the
// line number of a source file line and whose data
// is the offset (in bytes) from the beginning of the
// file. (e.g. files[3].line_seek[20] references the
// 20th line of the third source file.)
fc->ReadSource();
}
}
// Associate source files with the instructions they generated.
unsigned int addr;
for(addr = 0; addr<program_memory_size(); addr++) {
if( (program_memory[addr]->isa() != instruction::INVALID_INSTRUCTION)) {
FileContext *fc = files[program_memory[addr]->get_file_id()];
if(fc)
fc->put_address(program_memory[addr]->get_src_line(),
map_pm_index2address(addr));
}
}
// Associate the list file with
if (files.list_id() >= 0) {
// Parse the list file.
//printf("read_src_files List file:%d %d\n",files.list_id(),files.nsrc_files());
FileContext *fc = files[files.list_id()];
if (!fc)
return;
fc->ReadSource();
fc->rewind();
char buf[256];
int line = 1;
while(fc->gets(buf,sizeof(buf))) {
int address;
int opcode;
if (sscanf(buf,"%x %x",&address, &opcode) == 2) {
unsigned int uIndex = map_pm_address2index(address);
if (uIndex < program_memory_size()) {
program_memory[uIndex]->update_line_number(-1,-1,line,-1,-1);
fc->put_address(line,address);
}
}
line++;
}
}
}
//-------------------------------------------------------------------
//
// processor -- list
//
// Display the contents of either a source or list file
//
void Processor::list(unsigned int file_id,
unsigned int pc_val,
int start_line,
int end_line)
{
if(files.nsrc_files() == 0)
return;
if(pc_val > program_memory_size())
return;
if(program_memory[pc_val]->isa() == instruction::INVALID_INSTRUCTION)
{
cout << "There's no code at address 0x" << hex << pc_val << '\n';
return;
}
unsigned int line,pc_line;
if(file_id)
{
file_id = files.list_id();
line = program_memory[pc_val]->get_lst_line();
pc_line = program_memory[pc->value]->get_lst_line();
}
else
{
file_id = program_memory[pc_val]->get_file_id();
line = program_memory[pc_val]->get_src_line();
pc_line = program_memory[pc->value]->get_src_line();
}
start_line += line;
end_line += line;
FileContext *fc = files[file_id];
if(fc == NULL)
return;
start_line = (start_line < 0) ? 0 : start_line;
end_line = (end_line <= start_line) ? (start_line + 5) : end_line;
end_line = (end_line > (int)fc->max_line()) ? fc->max_line() : end_line;
cout << " listing " << fc->name() << " Starting line " << start_line
<< " Ending line " << end_line << '\n';
if (end_line == start_line)
return;
for(unsigned int i=start_line; i<=(unsigned int)end_line; i++)
{
char buf[256];
fc->ReadLine(i, buf, sizeof(buf));
if (pc_line == i)
cout << "==>";
else
cout << " ";
cout << buf;
}
}
static void trim(char * pBuffer) {
size_t iPos = 0;
char * pChar = pBuffer;
while(*pChar != 0 && ::isspace(*pChar)) {
pChar++;
}
if(pBuffer != pChar) {
memmove(pBuffer, pChar, strlen(pBuffer) - iPos);
}
iPos = strlen(pBuffer);
pChar = pBuffer + iPos - 1;
while( pChar > pBuffer && ::isspace(*pChar)) {
*pChar = 0;
pChar--;
}
}
//-------------------------------------------------------------------
//
// disassemble - Disassemble the contents of program memory from
// 'start_address' to 'end_address'. The instruction at the current
// PC is marked with an arrow '==>'. If an instruction has a break
// point set on it then it will be marked with a 'B'. The instruction
// mnemonics come from the class declarations for each instruction.
// However, it is possible to modify this on a per instruction basis.
// In other words, each instruction in the program memory has it's
// own instantiation. So a MOVWF at address 0x20 is different than
// one at address 0x21. It is possible to change the mnemonic of
// one without affecting the other. As of version 0.0.7 though, this
// is not implemented.
//
void Processor::disassemble (signed int s, signed int e)
{
instruction *inst;
int use_src_to_disasm = 0;
if(s > e)
return;
unsigned int start_PMindex = map_pm_address2index(s);
unsigned int end_PMindex = map_pm_address2index(e);
if(start_PMindex >= program_memory_size()) {
if(s <0)
start_PMindex = 0;
else
return;
}
if(end_PMindex >= program_memory_size()) {
if(e<0)
return;
else
end_PMindex = program_memory_size()-1;
}
const int iConsoleWidth = 80;
char str[iConsoleWidth];
char str2[iConsoleWidth];
if (!pc) {
printf("ERROR: Internal bug %s:%d\n",__FILE__,__LINE__);
exit(1);
}
unsigned uPCAddress = pc->get_value();
const char *pszPC;
char cBreak;
ISimConsole &Console = GetUserInterface().GetConsole();
int iLastFileId = -1;
FileContext *fc = NULL;
for(unsigned int PMindex = start_PMindex; PMindex<=end_PMindex; PMindex++) {
unsigned int uAddress = map_pm_index2address(PMindex);
str[0] = 0;
pszPC = (uPCAddress == uAddress) ? "==>" : " ";
inst = program_memory[PMindex];
// If this is not a "base" instruction then it has been replaced
// with something like a break point.
cBreak = ' ';
if(!inst->isBase()) {
cBreak = 'B';
inst = pma->get_base_instruction(PMindex);
}
if(inst->get_file_id() != -1) {
fc = files[inst->get_file_id()];
if(iLastFileId != inst->get_file_id())
Console.Printf("%s\n", fc->name().c_str());
iLastFileId = inst->get_file_id();
}
else
fc = 0;
const char *pLabel = get_symbol_table().findProgramAddressLabel(uAddress);
if(*pLabel != 0)
cout << pLabel << ":" << endl;
if(files.nsrc_files() && use_src_to_disasm) {
char buf[256];
files.ReadLine(inst->get_file_id(),
inst->get_src_line() - 1,
buf,
sizeof(buf));
cout << buf;
} else {
if(fc != NULL && inst->get_src_line() != -1) {
if(fc->ReadLine(inst->get_src_line(), str2, iConsoleWidth - 33)
!= NULL) {
trim(str2);
}
else {
str2[0] = 0;
}
}
else {
str2[0] = 0;
}
inst->name(str, sizeof(str));
char *pAfterNumonic = strchr(str, '\t');
int iNumonicWidth = pAfterNumonic ? pAfterNumonic - str : 5;
int iOperandsWidth = 14;
int iSrc = iOperandsWidth - (strlen(str) - iNumonicWidth - 1);
// Console.Printf("0.........1.........2.........3.........4.........5.........6.........7.........");
// Console.Printf("%d, strlen(str)=%d\n", iNumonicWidth, strlen(str));
const char *pFormat = (opcode_size() <=2) ? "% 3s%c%04x %04x %s %*s%s\n" : "% 3s%c%04x %06x %s %*s%s\n";
Console.Printf(pFormat,
pszPC, cBreak, uAddress, inst->get_opcode(),
str, iSrc, "", str2);
}
}
}
//-------------------------------------------------------------------
void Processor::save_state(FILE *fp)
{
if(!fp)
return;
unsigned int i;
fprintf(fp,"PROCESSOR:%s\n",name().c_str());
for(i=1; i<register_memory_size(); i++) {
Register *reg = rma.get_register(i);
if(reg && reg->isa() != Register::INVALID_REGISTER) {
fprintf(fp,"R:%X:%s:(%X,%X)\n",
reg->address,
reg->name().c_str(),
reg->value.get(),
reg->value.geti());
}
}
if(pc)
fprintf(fp,"P:0:PC:%X\n",pc->value);
}
//-------------------------------------------------------------------
void Processor::save_state(void)
{
unsigned int i;
for(i=0; i<register_memory_size(); i++) {
Register *reg = rma.get_register(i);
if(reg && reg->isa() != Register::INVALID_REGISTER) {
reg->put_trace_state(reg->getRV_notrace());
}
}
if(pc)
pc->put_trace_state(pc->value);
}
//-------------------------------------------------------------------
void Processor::load_state(FILE *fp)
{
if(!fp)
return;
cout << "Not implemented\n";
}
//-------------------------------------------------------------------
int ProgramMemoryAccess::find_closest_address_to_line(int file_id, int src_line)
{
int closest_address = -1;
if(!cpu)
return closest_address;
FileContext *fc = cpu->files[file_id];
if(fc)
{
int offset=0;
while((unsigned int)(src_line+offset)<fc->max_line())
{
closest_address = fc->get_address(src_line+offset);
if(closest_address>=0)
return closest_address;
offset++;
}
offset=-1;
while(src_line+offset>=0)
{
closest_address = fc->get_address(src_line+offset);
if(closest_address>=0)
return closest_address;
offset--;
}
}
return closest_address;
#if 0
int distance = cpu->program_memory_size();
for(int i = cpu->program_memory_size()-1; i>=0; i--) {
// Find the closet instruction to the src file line number
if( (cpu->program_memory[i]->isa() != instruction::INVALID_INSTRUCTION) &&
(cpu->program_memory[i]->get_file_id()==file_id) &&
(abs(cpu->program_memory[i]->get_src_line() - src_line) < distance))
{
distance = abs(cpu->program_memory[i]->get_src_line() - src_line);
closest_address = i;
if(distance == 0)
return cpu->map_pm_index2address(closest_address);
}
}
return cpu->map_pm_index2address(closest_address);
#endif
}
//-------------------------------------------------------------------
int ProgramMemoryAccess::find_address_from_line(FileContext *fc, int src_line)
{
return (cpu && fc) ? fc->get_address(src_line) : -1;
}
//--------------------------------------------------------------------------
//
// temporary - this could is going to get deleted as soon as file related
// stuff gets put into its own object/class.
void ProgramMemoryAccess::set_hll_mode(unsigned int new_hll_mode)
{
switch(new_hll_mode) {
case ASM_MODE:
hll_mode = ASM_MODE;
break;
case HLL_MODE:
hll_mode = HLL_MODE;
}
}
//--------------------------------------------------------------------------
unsigned int ProgramMemoryAccess::get_src_line(unsigned int address)
{
unsigned int line=0;
if(!cpu || !cpu->IsAddressInRange(address))
return INVALID_VALUE;
switch(get_hll_mode()) {
case ASM_MODE:
line = getFromAddress(address)->get_src_line();
break;
case HLL_MODE:
line = getFromAddress(address)->get_hll_src_line();
break;
}
return line;
}
//--------------------------------------------------------------------------
unsigned int ProgramMemoryAccess::get_file_id(unsigned int address)
{
if(!cpu)
return INVALID_VALUE;
switch(get_hll_mode()) {
case ASM_MODE:
return getFromAddress(address)->get_file_id();
break;
case HLL_MODE:
return getFromAddress(address)->get_hll_file_id();
break;
}
return INVALID_VALUE;
}
//-------------------------------------------------------------------
unsigned int ProgramMemoryAccess::set_break_at_address(unsigned int address)
{
if(hasValid_opcode_at_address(address))
return bp.set_execution_break(cpu, address);
return INVALID_VALUE;
}
//-------------------------------------------------------------------
unsigned int ProgramMemoryAccess::set_notify_at_address(unsigned int address, TriggerObject *cb)
{
if(hasValid_opcode_at_address(address))
return bp.set_notify_break(cpu, address, cb);
return INVALID_VALUE;
}
//-------------------------------------------------------------------
unsigned int ProgramMemoryAccess::set_profile_start_at_address(unsigned int address,
TriggerObject *cb)
{
unsigned int pm_index = cpu->map_pm_address2index(address);
if(pm_index<cpu->program_memory_size())
if (cpu->program_memory[pm_index]->isa() != instruction::INVALID_INSTRUCTION)
return bp.set_profile_start_break(cpu, address, cb);
return INVALID_VALUE;
}
//-------------------------------------------------------------------
unsigned int ProgramMemoryAccess::set_profile_stop_at_address(unsigned int address,
TriggerObject *cb)
{
if(hasValid_opcode_at_address(address))
return bp.set_profile_stop_break(cpu, address, cb);
return INVALID_VALUE;
}
//-------------------------------------------------------------------
int ProgramMemoryAccess::clear_break_at_address(unsigned int address,
enum instruction::INSTRUCTION_TYPES type =
instruction::BREAKPOINT_INSTRUCTION)
{
unsigned int uIndex = cpu->map_pm_address2index(address);
if( uIndex >= 0 && uIndex<cpu->program_memory_size()) {
instruction *instr = find_instruction(address,type);
if(instr!=0) {
int b = ((Breakpoint_Instruction *)instr)->bpn & BREAKPOINT_MASK;
// this actually removes the object
bp.clear( b );
return 1;
}
}
return 0;
}
//-------------------------------------------------------------------
int ProgramMemoryAccess::clear_break_at_address(unsigned int address,
instruction * pInstruction) {
if(!cpu || !cpu->IsAddressInRange(address))
return -1;
instruction **ppAddressLocation = &cpu->program_memory[cpu->map_pm_address2index(address)];
Breakpoint_Instruction *br = dynamic_cast<Breakpoint_Instruction *>(*ppAddressLocation);
if (br == pInstruction) {
// at the head of the chain
*ppAddressLocation = br->getReplaced();
}
else {
Breakpoint_Instruction *pLast = br;
// Find myself in the chain
while(br != NULL) {
if (br == pInstruction) {
// found
pLast->setReplaced(br->getReplaced());
br->setReplaced(0);
return 1;
}
else {
pLast = br;
br = dynamic_cast<Breakpoint_Instruction *>(br->getReplaced());
}
}
}
return 0;
}
//-------------------------------------------------------------------
int ProgramMemoryAccess::clear_notify_at_address(unsigned int address)
{
return clear_break_at_address(address,instruction::NOTIFY_INSTRUCTION);
}
//-------------------------------------------------------------------
int ProgramMemoryAccess::clear_profile_start_at_address(unsigned int address)
{
return clear_break_at_address(address,instruction::PROFILE_START_INSTRUCTION);
}
//-------------------------------------------------------------------
int ProgramMemoryAccess::clear_profile_stop_at_address(unsigned int address)
{
return clear_break_at_address(address,instruction::PROFILE_STOP_INSTRUCTION);
}
//-------------------------------------------------------------------
int ProgramMemoryAccess::address_has_break(unsigned int address,
enum instruction::INSTRUCTION_TYPES type)
{
if(find_instruction(address,type)!=0)
return 1;
return 0;
}
//-------------------------------------------------------------------
int ProgramMemoryAccess::address_has_notify(unsigned int address)
{
return address_has_break(address,instruction::NOTIFY_INSTRUCTION);
}
//-------------------------------------------------------------------
int ProgramMemoryAccess::address_has_profile_start(unsigned int address)
{
return address_has_break(address,instruction::PROFILE_START_INSTRUCTION);
}
//-------------------------------------------------------------------
int ProgramMemoryAccess::address_has_profile_stop(unsigned int address)
{
return address_has_break(address,instruction::PROFILE_STOP_INSTRUCTION);
}
//-------------------------------------------------------------------
void ProgramMemoryAccess::toggle_break_at_address(unsigned int address)
{
if(address_has_break(address))
clear_break_at_address(address);
else
set_break_at_address(address);
}
//-------------------------------------------------------------------
void ProgramMemoryAccess::set_break_at_line(unsigned int file_id, unsigned int src_line)
{
int address;
if( (address = find_closest_address_to_line(file_id, src_line)) >= 0)
set_break_at_address(address);
}
void ProgramMemoryAccess::clear_break_at_line(unsigned int file_id, unsigned int src_line)
{
int address;
if( (address = find_closest_address_to_line(file_id, src_line)) >= 0)
clear_break_at_address(address);
}
void ProgramMemoryAccess::toggle_break_at_line(unsigned int file_id, unsigned int src_line)
{
toggle_break_at_address(find_closest_address_to_line(file_id, src_line));
}
//-------------------------------------------------------------------
//
Processor * Processor::construct(void)
{
cout << " Can't create a generic processor\n";
return 0;
}
//-------------------------------------------------------------------
void Processor::trace_dump(int type, int amount)
{
trace.dump(amount, stdout);
}
//-------------------------------------------------------------------
// Decode a single trace item
int Processor::trace_dump1(int type, char *buffer, int bufsize)
{
snprintf(buffer, bufsize,"*** INVALID TRACE *** 0x%x",type);
return 1;
}
//-------------------------------------------------------------------
// getWriteTT
//
// For devices with more than 64k of registers (which are not supported
// at the moment), we can enhance this function to return different Trace
// type objects base on the upper address bits.
RegisterValue Processor::getWriteTT(unsigned int j)
{
if(!writeTT) {
writeTT = new RegisterWriteTraceType(this,2);
trace.allocateTraceType(writeTT);
}
// The upper 8-bits define the dynamically allocated trace type
// The lower 8-bits will record the register value that is written.
// The middle 16-bits are the register address
unsigned int tt = (writeTT->type() & 0xff000000) | ((j & 0xffff) << 8);
return RegisterValue(tt, tt + (1<<24) );
}
RegisterValue Processor::getReadTT(unsigned int j)
{
if(!readTT) {
readTT = new RegisterReadTraceType(this,2);
trace.allocateTraceType(readTT);
}
// The upper 8-bits define the dynamically allocated trace type
// The lower 8-bits will record the register value that is written.
// The middle 16-bits are the register address
unsigned int tt = (readTT->type() & 0xff000000) | ((j & 0xffff) << 8);
return RegisterValue(tt, tt + (1<<24) );
}
//-------------------------------------------------------------------
//
// run -- Begin simulating and don't stop until there is a break.
// FIXME - shouldn't this be a pure virtual function?
void Processor::run (bool refresh)
{
cout << __FUNCTION__ << endl;
}
//-------------------------------------------------------------------
//
// step - Simulate one (or more) instructions. If a breakpoint is set
// at the current PC-> 'step' will go right through it. (That's supposed
// to be a feature.)
//
void Processor::step (unsigned int steps, bool refresh)
{
if(!steps)
return;
if(simulation_mode != eSM_STOPPED) {
if(verbose)
cout << "Ignoring step request because simulation is not stopped\n";
return;
}
simulation_mode = eSM_SINGLE_STEPPING;
do
{
if(bp.have_sleep() || bp.have_pm_write())
{
// If we are sleeping or writing to the program memory (18cxxx only)
// then step one cycle - but don't execute any code
get_cycles().increment();
if(refresh)
trace_dump(0,1);
}
else if(bp.have_interrupt())
{
interrupt();
}
else
{
step_one(refresh);
get_trace().cycle_counter(get_cycles().value);
if(refresh)
trace_dump(0,1);
}
}
while(!bp.have_halt() && --steps>0);
bp.clear_halt();
simulation_mode = eSM_STOPPED;
if(refresh)
get_interface().simulation_has_stopped();
}
//-------------------------------------------------------------------
//
// step_over - In most cases, step_over will simulate just one instruction.
// However, if the next instruction is a branching one (e.g. goto, call,
// return, etc.) then a break point will be set after it and gpsim will
// begin 'running'. This is useful for stepping over time-consuming calls.
//
void Processor::step_over (bool refresh)
{
step(1,refresh); // Try one step
}
//-------------------------------------------------------------------
//
// finish
//
// this method really only applies to processors with stacks.
void Processor::finish(void)
{
}
//-------------------------------------------------------------------
void Processor::run_to_address (unsigned int destination)
{
if(simulation_mode != eSM_STOPPED) {
if(verbose)
cout << "Ignoring run-to-address request because simulation is not stopped\n";
return;
}
// Set a temporary break point
unsigned int bp_num = bp.set_execution_break(this, destination);
run();
bp.clear(bp_num);
}
//-------------------------------------------------------------------
//
//
// create
//
// The purpose of this member function is to 'create' a pic processor.
// Since this is a base class member function, only those things that
// are common to all pics are created.
void Processor::create (void)
{
cout << " a generic processor cannot be created " << __FILE__ << __LINE__ <<endl;
exit(1);
}
//-------------------------------------------------------------------
void Processor::dump_registers (void)
{
// parse_string("dump");
}
//-------------------------------------------------------------------
void Processor::Debug()
{
cout << " === Debug === \n";
if(pc)
cout << "PC=0x"<<hex << pc->value << endl;
}
//-------------------------------------------------------------------
instruction *ProgramMemoryAccess::find_instruction(unsigned int address,
enum instruction::INSTRUCTION_TYPES type)
{
unsigned int uIndex = cpu->map_pm_address2index(address);
if(cpu->program_memory_size()<=uIndex)
return 0;
instruction *p = getFromIndex(uIndex);
if(p->isa()==instruction::INVALID_INSTRUCTION)
return 0;
for(;;)
{
if(p->isa()==type)
return p;
switch(p->isa())
{
case instruction::MULTIWORD_INSTRUCTION:
case instruction::INVALID_INSTRUCTION:
case instruction::NORMAL_INSTRUCTION:
return 0;
case instruction::BREAKPOINT_INSTRUCTION:
case instruction::NOTIFY_INSTRUCTION:
case instruction::PROFILE_START_INSTRUCTION:
case instruction::PROFILE_STOP_INSTRUCTION:
case instruction::ASSERTION_INSTRUCTION:
p=((Breakpoint_Instruction *)p)->getReplaced();
break;
}
}
return 0;
}
//-------------------------------------------------------------------
guint64 Processor::cycles_used(unsigned int address)
{
return program_memory[address]->getCyclesUsed();
}
//-------------------------------------------------------------------
MemoryAccess::MemoryAccess(Processor *new_cpu)
{
cpu = new_cpu;
}
Processor *MemoryAccess::get_cpu(void)
{
return cpu;
}
void MemoryAccess::set_cpu(Processor *p)
{
cpu = p;
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// Program memory interface used by the command line
class ProgramMemoryCollection : public IIndexedCollection
{
public:
ProgramMemoryCollection(Processor *pProcessor,
const char *collection_name,
ProgramMemoryAccess *pPma);
virtual unsigned int GetSize();
virtual Value &GetAt(unsigned int uAddress, Value *pValue=0);
virtual void SetAt(unsigned int uAddress, Value *pValue);
virtual void ConsolidateValues(int &iColumnWidth,
vector<string> &aList,
vector<string> &aValue);
virtual unsigned int GetLowerBound();
virtual unsigned int GetUpperBound();
virtual bool bIsIndexInRange(unsigned int uIndex);
private:
Processor * m_pProcessor;
ProgramMemoryAccess *m_pPma;
Integer m_ReturnValue;
};
ProgramMemoryCollection::ProgramMemoryCollection (Processor *pProcessor,
const char *pC_collection_name,
ProgramMemoryAccess *pPma) :
IIndexedCollection(16), m_ReturnValue(0)
{
m_pProcessor = pProcessor;
Value::new_name(pC_collection_name);
m_pPma = pPma;
get_symbol_table().add(this);
}
unsigned int ProgramMemoryCollection::GetSize()
{
return m_pProcessor->map_pm_address2index(m_pProcessor->program_memory_size());
}
Value &ProgramMemoryCollection::GetAt(unsigned int uAddress, Value *)
{
//m_pProcessor->map_pm_address2index
m_ReturnValue.set((int)m_pPma->get_rom(uAddress));
m_ReturnValue.setBitmask( (1<<(m_pProcessor->opcode_size()*8)) - 1);
ostringstream sIndex;
sIndex << Value::name() << "[" << hex << m_szPrefix << uAddress << "]" << '\000';
m_ReturnValue.new_name(sIndex.str().c_str());
return m_ReturnValue;
}
void ProgramMemoryCollection::SetAt(unsigned int uAddress, Value *pValue)
{
Integer *pInt = dynamic_cast<Integer*>(pValue);
if(pInt == NULL) {
throw Error("rValue is not an Integer");
}
else {
m_pPma->put_rom(uAddress, (unsigned int)(int)*pInt);
}
}
void ProgramMemoryCollection::ConsolidateValues(int &iColumnWidth,
vector<string> &aList,
vector<string> &aValue)
{
unsigned int uFirstAddress = 0;
unsigned int uAddress;
//Register * pReg = m_ppRegisters[0];
//Integer uLastValue(pReg->getRV_notrace().data);
Integer uLastValue(m_pPma->get_opcode(0));
uLastValue.setBitmask((1<<(m_pProcessor->opcode_size()*8)) - 1);
unsigned int uSize = GetSize();
for(uAddress = 0; uAddress < uSize; uAddress++) {
ostringstream sIndex;
unsigned int ui_opcode = m_pPma->get_opcode(uAddress);
if((unsigned int)uLastValue != ui_opcode) {
PushValue(uFirstAddress, uAddress,
&uLastValue, aList, aValue);
iColumnWidth = max(iColumnWidth, (int)aList.back().size());
uFirstAddress = uAddress;
uLastValue = ui_opcode;
}
}
uAddress--;
// Record the last set of elements
if(uFirstAddress <= uAddress) {
PushValue(uFirstAddress, uAddress,
&uLastValue, aList, aValue);
iColumnWidth = max(iColumnWidth, (int)aList.back().size());
}
}
//void RegisterCollection::SetAt(ExprList_t* pIndexers, Expression *pExpr) {
// throw Error("RegisterCollection::SetAt() not implemented");
//}
unsigned int ProgramMemoryCollection::GetLowerBound()
{
return 0;
}
unsigned int ProgramMemoryCollection::GetUpperBound()
{
return GetSize() - 1;
}
bool ProgramMemoryCollection::bIsIndexInRange(unsigned int uAddress)
{
return m_pPma->get_rom(uAddress) != 0xffffffff;
// (uIndex >= GetLowerBound() && uIndex <= GetUpperBound()) ||
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
//
// ProgramMemoryAccess
//
// The ProgramMemoryAccess class provides an interface to the processor's
// program memory. On Pic processors, this is the memory where instructions
// are stored.
//
ProgramMemoryAccess::ProgramMemoryAccess(Processor *new_cpu) :
MemoryAccess(new_cpu)
{
init(new_cpu);
m_pRomCollection = new ProgramMemoryCollection(new_cpu,
"romData",
this);
}
void ProgramMemoryAccess::init(Processor *new_cpu)
{
set_cpu(new_cpu);
_address = _opcode = _state = 0;
hll_mode = ASM_MODE;
// add the 'main' pma to the list pma context's. Processors may
// choose to add multiple pma's to the context list. The gui
// will build a source browser for each one of these. The purpose
// is to provide more than one way of debugging the code. (e.g.
// this is useful for debugging interrupt versus non-interrupt code).
if(cpu)
cpu->pma_context.push_back(this);
}
/*
void ProgramMemoryAccess::name(string & new_name)
{
name_str = new_name;
}
*/
void ProgramMemoryAccess::putToAddress(unsigned int address, instruction *new_instruction)
{
putToIndex(cpu->map_pm_address2index(address), new_instruction);
}
void ProgramMemoryAccess::putToIndex(unsigned int uIndex, instruction *new_instruction)
{
if(!new_instruction)
return;
//??? TSD 15MAY06 why would you care if the instruction you're replacing is valid or not?
//??? if(hasValid_opcode_at_index(uIndex)) {
cpu->program_memory[uIndex] = new_instruction;
new_instruction->update();
//???}
}
void ProgramMemoryAccess::remove(unsigned int address, instruction *bp_instruction)
{
if(!bp_instruction)
return;
instruction *instr = cpu->program_memory[cpu->map_pm_address2index(address)];
if (typeid(Breakpoint_Instruction) == typeid(*instr) ||
typeid(RegisterAssertion) == typeid(*instr)) {
Breakpoint_Instruction* toRemove = (Breakpoint_Instruction*)bp_instruction;
Breakpoint_Instruction *last = (Breakpoint_Instruction*)instr;
if (toRemove == last) {
cpu->program_memory[cpu->map_pm_address2index(address)] = last->getReplaced();
return;
}
do {
if(typeid(Breakpoint_Instruction) != typeid(*last->getReplaced()) &&
typeid(RegisterAssertion) != typeid(*last->getReplaced()))
// not found
return;
Breakpoint_Instruction *replaced = (Breakpoint_Instruction*)last->getReplaced();
if(toRemove == replaced) {
// remove from the chain
last->setReplaced(replaced->getReplaced());
return;
}
last = replaced;
}
while(typeid(Breakpoint_Instruction) != typeid(*last));
}
// if we get here the object was not in the list or was
// not a Breakpoint_Instruction
// assert(typeid(*instr) != typeid(Breakpoint_Instruction));
}
instruction *ProgramMemoryAccess::getFromAddress(unsigned int address)
{
if(!cpu || !cpu->IsAddressInRange(address))
return &bad_instruction;
unsigned int uIndex = cpu->map_pm_address2index(address);
return getFromIndex(uIndex);
}
instruction *ProgramMemoryAccess::getFromIndex(unsigned int uIndex)
{
if(uIndex < cpu->program_memory_size())
return cpu->program_memory[uIndex];
else
return 0;
}
// like get, but will ignore instruction break points
instruction *ProgramMemoryAccess::get_base_instruction(unsigned int uIndex)
{
instruction *p;
p=getFromIndex(uIndex);
if(p==0)
return 0;
for(;;)
{
switch(p->isa())
{
case instruction::MULTIWORD_INSTRUCTION:
case instruction::INVALID_INSTRUCTION:
case instruction::NORMAL_INSTRUCTION:
return p;
case instruction::BREAKPOINT_INSTRUCTION:
case instruction::NOTIFY_INSTRUCTION:
case instruction::PROFILE_START_INSTRUCTION:
case instruction::PROFILE_STOP_INSTRUCTION:
case instruction::ASSERTION_INSTRUCTION:
p=((Breakpoint_Instruction *)p)->getReplaced();
break;
}
}
return 0;
}
//----------------------------------------
// get_rom - return the rom contents from program memory
// If the address is normal program memory, then the opcode
// of the instruction at that address is returned.
// If the address is some other special memory (like configuration
// memory in a PIC) then that data is returned instead.
unsigned int ProgramMemoryAccess::get_rom(unsigned int addr)
{
return cpu->get_program_memory_at_address(addr);
}
//----------------------------------------
// put_rom - write new data to the program memory.
// If the address is in normal program memory, then a new instruction
// will be generated (if possible). If the address is some other
// special memory (like configuration memory), then that area will
// be updated.
//
void ProgramMemoryAccess::put_rom(unsigned int addr,unsigned int value)
{
return cpu->init_program_memory(addr,value);
}
//----------------------------------------
// get_opcode - return an opcode from program memory.
// If the address is out of range return 0.
unsigned int ProgramMemoryAccess::get_opcode(unsigned int addr)
{
instruction * pInstr = getFromAddress(addr);
if(pInstr != 0)
return pInstr->get_opcode();
else
return 0;
}
//----------------------------------------
// get_opcode_name - return an opcode name from program memory.
// If the address is out of range return 0;
char *ProgramMemoryAccess::get_opcode_name(unsigned int addr, char *buffer, unsigned int size)
{
unsigned int uIndex = cpu->map_pm_address2index(addr);
if(uIndex < cpu->program_memory_size())
return cpu->program_memory[uIndex]->name(buffer,size);
*buffer = 0;
return 0;
}
//----------------------------------------
// Get the current value of the program counter.
unsigned int ProgramMemoryAccess::get_PC(void)
{
if(cpu && cpu->pc)
return cpu->pc->get_value();
return 0;
}
//----------------------------------------
// Get the current value of the program counter.
void ProgramMemoryAccess::set_PC(unsigned int new_pc)
{
if(cpu && cpu->pc)
return cpu->pc->put_value(new_pc);
}
Program_Counter *ProgramMemoryAccess::GetProgramCounter(void)
{
if(cpu)
return cpu->pc;
return 0;
}
void ProgramMemoryAccess::put_opcode_start(unsigned int addr, unsigned int new_opcode)
{
unsigned int uIndex = cpu->map_pm_address2index(addr);
if( (uIndex < cpu->program_memory_size()) && (_state == 0))
{
_state = 1;
_address = addr;
_opcode = new_opcode;
get_cycles().set_break_delta(40000, this);
bp.set_pm_write();
}
}
void ProgramMemoryAccess::put_opcode(unsigned int addr, unsigned int new_opcode)
{
unsigned int uIndex = cpu->map_pm_address2index(addr);
if(uIndex >= cpu->program_memory_size())
return;
instruction *old_inst = get_base_instruction(uIndex);
instruction *new_inst = cpu->disasm(addr,new_opcode);
if(new_inst==0)
{
puts("FIXME, in ProgramMemoryAccess::put_opcode");
return;
}
if(!old_inst) {
putToIndex(uIndex,new_inst);
return;
}
if(old_inst->isa() == instruction::INVALID_INSTRUCTION) {
putToIndex(uIndex,new_inst);
return;
}
// Now we need to make sure that the instruction we are replacing is
// not a multi-word instruction. The 12 and 14 bit cores don't have
// multi-word instructions, but the 16 bit cores do. If we are replacing
// the second word of a multiword instruction, then we only need to
// 'uninitialize' it.
// if there was a breakpoint set at addr, save a pointer to the breakpoint.
Breakpoint_Instruction *b=bpi;
instruction *prev = get_base_instruction(cpu->map_pm_address2index(addr-1));
if(prev)
prev->initialize(false);
new_inst->update_line_number(old_inst->get_file_id(),
old_inst->get_src_line(),
old_inst->get_lst_line(),
old_inst->get_hll_src_line(),
old_inst->get_hll_file_id());
//new_inst->xref = old_inst->xref;
if(b)
b->setReplaced(new_inst);
else
cpu->program_memory[uIndex] = new_inst;
cpu->program_memory[uIndex]->setModified(true);
//if(cpu->program_memory[addr]->xref)
cpu->program_memory[uIndex]->update();
delete(old_inst);
}
//--------------------------------------------------------------------------
void ProgramMemoryAccess::assign_xref(unsigned int address, gpointer xref)
{
instruction &q = *getFromAddress(address);
if(q.isa()==instruction::INVALID_INSTRUCTION)
return;
q.add_xref(xref);
}
//--------------------------------------------------------------------------
void ProgramMemoryAccess::step(unsigned int steps,bool refresh)
{
if(!cpu)
return;
switch(get_hll_mode()) {
case ASM_MODE:
cpu->step(steps,refresh);
break;
case HLL_MODE:
{
unsigned int initial_line = cpu->pma->get_src_line(cpu->pc->get_value());
unsigned int initial_pc = cpu->pc->get_value();
while(1) {
cpu->step(1,false);
if(( cpu->pc->get_value() == initial_pc) ||
(get_src_line(cpu->pc->get_value()) != initial_line)) {
if(refresh)
get_interface().simulation_has_stopped();
break;
}
}
break;
}
}
}
//--------------------------------------------------------------------------
void ProgramMemoryAccess::step_over(bool refresh)
{
if(!cpu)
return;
switch(get_hll_mode()) {
case ASM_MODE:
cpu->step_over(refresh);
break;
case HLL_MODE:
{
#if 0
//
// High level language step-overs are only supported for
// pic processors
//
pic_processor *pic = dynamic_cast<pic_processor *>cpu;
if(!pic) {
cout << "step-over is not supported for non-PIC processors\n";
return;
}
unsigned int initial_line = cpu->pma.get_src_line(cpu->pc->get_value());
unsigned int initial_pc = cpu->pc->get_value();
unsigned int initial_stack_depth = pic->stack->pointer&pic->stack->stack_mask;
while(1)
{
step(1);
if(initial_stack_depth < pic->stack->pointer&pic->stack->stack_mask)
gpsim_finish(processor_id);
if(pic->pc->get_value()==initial_pc)
break;
if(get_src_line(initial_pc) != initial_line)
{
if(gpsim_get_file_id(processor_id, pic->pc->get_value()))
break;
}
}
break;
#endif
cout << "HLL Step is not supported\n";
}
}
}
//--------------------------------------------------------------------------
void ProgramMemoryAccess::run(bool refresh)
{
cpu->run(refresh);
}
//--------------------------------------------------------------------------
void ProgramMemoryAccess::stop(void)
{
bp.halt();
}
//--------------------------------------------------------------------------
void ProgramMemoryAccess::finish(void)
{
cpu->finish();
}
//--------------------------------------------------------------------------
bool ProgramMemoryAccess::hasValid_opcode_at_address(unsigned int address)
{
if(getFromAddress(address)->isa() != instruction::INVALID_INSTRUCTION)
return true;
return false;
}
bool ProgramMemoryAccess::hasValid_opcode_at_index(unsigned int uIndex)
{
if((getFromIndex(uIndex))->isa() != instruction::INVALID_INSTRUCTION)
return true;
return false;
}
//--------------------------------------------------------------------------
bool ProgramMemoryAccess::isModified(unsigned int address)
{
if((address < cpu->program_memory_size()) &&
cpu->program_memory[address]->bIsModified())
return true;
return false;
}
//========================================================================
// Register Memory Access
RegisterMemoryAccess::RegisterMemoryAccess(Processor *new_cpu) :
MemoryAccess(new_cpu)
{
cpu = 0;
registers = 0;
nRegisters = 0;
}
RegisterMemoryAccess::~RegisterMemoryAccess()
{
// registers memory is owned by the Processor class
}
//--------------------------------------------------------------------------
Register *RegisterMemoryAccess::get_register(unsigned int address)
{
if(!cpu || !registers || nRegisters<=address)
return 0;
Register *reg = registers[address];
// If there are breakpoints set on the register, then drill down
// through them until we get to the real register.
return reg ? reg->getReg() : 0;
}
//--------------------------------------------------------------------------
void RegisterMemoryAccess::set_Registers(Register **_registers, int _nRegisters)
{
nRegisters = _nRegisters;
registers = _registers;
}
//------------------------------------------------------------------------
// insertRegister - Each register address may contain a linked list of registers.
// The top most register is the one that is referenced whenever a processor
// accesses the register memory. The primary purpose of the linked list is to
// support register breakpoints. For example, a write breakpoint is implemented
// with a breakpoint class derived from the register class. Setting a write
// breakpoint involves creating the write breakpoint object and placing it
// at the top of the register linked list. Then, when a processor attempts
// to write to this register, the breakpoint object will capture this and
// halt the simulation.
bool RegisterMemoryAccess::insertRegister(int address, Register *pReg)
{
if(!cpu || !registers || nRegisters<=address ||!pReg)
return false;
Register *ptop = registers[address];
pReg->setReplaced(ptop);
registers[address] = pReg;
return true;
}
//------------------------------------------------------------------------
// removeRegister - see comment on insertRegister. This method removes
// a register object from the breakpoint linked list.
bool RegisterMemoryAccess::removeRegister(int address, Register *pReg)
{
if(!cpu || !registers || nRegisters<=address ||!pReg)
return false;
Register *ptop = registers[address];
if (ptop == pReg && pReg->getReplaced())
registers[address] = pReg->getReplaced();
else
while (ptop) {
Register *pNext = ptop->getReplaced();
if (pNext == pReg) {
ptop->setReplaced(pNext->getReplaced());
return true;
}
ptop = pNext;
}
return false;
}
//-------------------------------------------------------------------
bool RegisterMemoryAccess::hasBreak(unsigned int address)
{
if(!cpu || !registers || nRegisters<=address)
return false;
return registers[address]->isa() == Register::BP_REGISTER;
}
static InvalidRegister AnInvalidRegister;
//-------------------------------------------------------------------
Register &RegisterMemoryAccess::operator [] (unsigned int address)
{
if(!registers || get_size()<=address)
return AnInvalidRegister;
return *registers[address];
}
//========================================================================
// Processor Constructor
ProcessorConstructorList *ProcessorConstructorList::processor_list;
ProcessorConstructorList * ProcessorConstructorList::GetList() {
return processor_list = ProcessorConstructor::GetList();
}
ProcessorConstructor::ProcessorConstructor(tCpuContructor _cpu_constructor,
const char *name1,
const char *name2,
const char *name3,
const char *name4)
{
cpu_constructor = _cpu_constructor; // Pointer to the processor constructor
names[0] = name1; // First name
names[1] = name2; // and three aliases...
names[2] = name3;
names[3] = name4;
// Add the processor to the list of supported processors:
GetList()->push_back(this);
}
//------------------------------------------------------------
Processor * ProcessorConstructor::ConstructProcessor(const char *opt_name)
{
// Instantiate a specific processor. If a name is provided, then that
// will be used. Otherwise, the third name in the list of aliases for
// this processor will be used instead. (Why 3rd?... Before optional
// processor names were allowed, the default name matched what is now
// the third alias; this maintains a backward compatibility).
if (opt_name && strlen(opt_name))
return cpu_constructor(opt_name);
return cpu_constructor(names[2]);
}
ProcessorConstructorList * ProcessorConstructor::processor_list;
ProcessorConstructorList * ProcessorConstructor::GetList()
{
if(processor_list == NULL) {
processor_list = new ProcessorConstructorList();
}
return processor_list;
}
//------------------------------------------------------------
// findByType -- search through the list of supported processors for
// the one matching 'name'.
ProcessorConstructor *ProcessorConstructorList::findByType(const char *name)
{
ProcessorConstructorList::iterator processor_iterator;
for (processor_iterator = processor_list->begin();
processor_iterator != processor_list->end();
++processor_iterator) {
ProcessorConstructor *p = *processor_iterator;
for(int j=0; j<nProcessorNames; j++)
if(p->names[j] && strcmp(name,p->names[j]) == 0)
return p;
}
return 0;
}
//------------------------------------------------------------
// dump() -- Print out a list of all of the processors
//
string ProcessorConstructorList::DisplayString(void)
{
ostringstream stream;
list <ProcessorConstructor *> :: iterator processor_iterator;
const int nPerRow = 4; // Number of names to print per row.
int i,j,k,longest;
ProcessorConstructor *p;
// loop through all of the processors and find the
// one with the longest name
longest = 0;
for (processor_iterator = processor_list->begin();
processor_iterator != processor_list->end();
processor_iterator++) {
p = *processor_iterator;
k = strlen(p->names[1]);
if(k>longest)
longest = k;
}
// Print the name of each processor.
for (processor_iterator = processor_list->begin();
processor_iterator != processor_list->end(); ) {
for(i=0; i<nPerRow && processor_iterator != processor_list->end(); i++) {
p = *processor_iterator++;
stream << p->names[1];
if(i<nPerRow-1) {
// if this is not the last processor in the column, then
// pad a few spaces to align the columns.
k = longest + 2 - strlen(p->names[1]);
for(j=0; j<k; j++)
stream << ' ';
}
}
stream << endl;
}
stream << ends;
return string(stream.str());
}
//------------------------------------------------------------------------
FileContext::FileContext(string &new_name)
{
name_str = new_name;
fptr = NULL;
line_seek = 0;
m_uiMaxLine = 0;
pm_address = 0;
m_bIsList = false;
}
FileContext::FileContext(const char *new_name)
{
name_str = string(new_name);
fptr = NULL;
line_seek = 0;
m_uiMaxLine = 0;
pm_address = 0;
m_bIsList = false;
}
FileContext::~FileContext(void)
{
delete line_seek;
delete pm_address;
}
//----------------------------------------
// ReadSource
//
// This will open the file for this FileContext
// and fill the line_seek vector with the file
// positions corresponding to the start of every
// source line in the file.
//
// e.g. lineseek[20] describes where the 20'th source
// line is in the file.
void FileContext::ReadSource(void)
{
if( (max_line() <= 0) || name_str.length() == 0)
return;
const char *str = name_str.c_str();
// If the file is not open yet, then try to open it.
if(!fptr)
fptr = fopen_path(str,"r");
// If the file still isn't open, then we have a problem
// FIXME - should some corrective action be taken?
if(!fptr) {
cout << "Unable to open " << str << endl;
return;
}
if(line_seek)
delete line_seek;
line_seek = new vector<int>(max_line()+1);
pm_address = new vector<int>(max_line()+1);
std::rewind(fptr);
char buf[256],*s;
(*line_seek)[0] = 0;
for(unsigned int j=1; j<=max_line(); j++) {
(*pm_address)[j] = -1;
(*line_seek)[j] = ftell(fptr);
s = fgets(buf,256,fptr);
if(s != buf)
break;
}
}
//----------------------------------------
// ReadLine
//
// Read one line from a source file.
char *FileContext::ReadLine(unsigned int line_number, char *buf, unsigned int nBytes)
{
if(!fptr)
return 0;
fseek(fptr,
(*line_seek)[line_number],
SEEK_SET);
return fgets(buf, nBytes, fptr);
}
//----------------------------------------
//
char *FileContext::gets(char *buf, unsigned int nBytes)
{
if(!fptr)
return 0;
return fgets(buf, nBytes, fptr);
}
//----------------------------------------
unsigned int FileContext::max_line()
{
if(fptr && !m_uiMaxLine) {
char buff[256];
rewind();
m_uiMaxLine=0;
while(fgets(buff,sizeof(buff),fptr))
m_uiMaxLine++;
}
return m_uiMaxLine;
}
//----------------------------------------
void FileContext::rewind()
{
if(fptr)
fseek(fptr,0,SEEK_SET);
}
//----------------------------------------
void FileContext::open(const char *mode)
{
if(!fptr) {
fptr = fopen_path(name_str.c_str(), mode);
max_line();
}
}
//----------------------------------------
void FileContext::close()
{
if(fptr != NULL) {
fclose(fptr);
fptr = NULL;
}
}
//----------------------------------------
int FileContext::get_address(unsigned int line_number)
{
if(line_number <= max_line() && pm_address)
return (*pm_address)[line_number];
return -1;
}
//----------------------------------------
void FileContext::put_address(unsigned int line_number, unsigned int address)
{
if(line_number <= max_line() && pm_address && (*pm_address)[line_number]<0)
(*pm_address)[line_number] = address;
}
//------------------------------------------------------------------------
FileContextList::FileContextList()
{
lastFile=0;
list_file_id = -1; // assume that no list file is present.
}
FileContextList::~FileContextList(void)
{
FileContextList::iterator it;
FileContextList::iterator itEnd = end();
for (it = begin(); it != itEnd; it++)
it->close();
}
static bool EndsWith(string &sSubject, string &sSubstring)
{
if(sSubject.size() < sSubstring.size()) {
return false;
}
else {
string sSubjectEnding = sSubject.substr(sSubject.size() -
sSubstring.size());
return sSubjectEnding == sSubstring;
}
}
int FileContextList::Find(string &fname)
{
if(lastFile) {
for (int i = 0; i < lastFile; i++) {
if(EndsWith((*this)[i]->name(), fname)) {
return i;
}
}
}
return -1;
}
extern bool bHasAbsolutePath(string &fname);
int FileContextList::Add(string &new_name)
{
string sFull = bHasAbsolutePath(new_name) ? new_name : (sSourcePath + new_name);
push_back(FileContext(sFull));
lastFile++;
if(CSimulationContext::GetContext()->IsSourceEnabled()) {
back().open("r");
if(verbose)
cout << "Added new file named: " << new_name
<< " id = " << lastFile << endl;
}
return lastFile-1;
}
int FileContextList::Add(const char *new_name)
{
string sNewName(new_name);
return Add (sNewName);
}
FileContext *FileContextList::operator [] (int file_id)
{
if(file_id<0 || file_id >= lastFile)
return 0;
return &this->_Myt::at(file_id);
}
char *FileContextList::ReadLine(int file_id, int line_number, char *buf, int nBytes)
{
FileContext *fc = operator[](file_id);
if(fc)
return fc->ReadLine(line_number, buf, nBytes);
return 0;
}
//----------------------------------------
//
char *FileContextList::gets(int file_id, char *buf, int nBytes)
{
FileContext *fc = operator[](file_id);
if(fc)
return fc->gets(buf, nBytes);
return 0;
}
//----------------------------------------
void FileContextList::rewind(int file_id)
{
FileContext *fc = operator[](file_id);
if(fc)
return fc->rewind();
}
extern void EnsureTrailingFolderDelimiter(string &sPath);
extern void SplitPathAndFile(string &sSource, string &sFolder, string &sFile);
//----------------------------------------
void FileContextList::SetSourcePath(const char *pPath) {
string sPath(pPath);
string sFolder, sFile;
SplitPathAndFile(sPath, sSourcePath, sFile);
EnsureTrailingFolderDelimiter(sSourcePath);
}
//----------------------------------------
//
void FileContextList::list_id(int new_list_id)
{
FileContext *fc = operator[](list_file_id);
if (fc)
fc->setListId(false);
list_file_id = new_list_id;
fc = operator[](list_file_id);
if (fc)
fc->setListId(true);
}
syntax highlighted by Code2HTML, v. 0.9.1