/* 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 #ifdef _WIN32 #include "uxtime.h" #else #include #endif #include #include #include #include #include "../config.h" #include "gpsim_def.h" #include #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; ialias_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"<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; iget_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; imax_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; addrisa() != 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; iisa() != 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; iisa() != 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)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_indexprogram_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 && uIndexprogram_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(*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(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__ <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 &aList, vector &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(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 &aList, vector &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_castcpu; 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; jnames[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 :: 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; iend(); i++) { p = *processor_iterator++; stream << p->names[1]; if(inames[1]); for(j=0; j(max_line()+1); pm_address = new vector(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); }