/* `Deep Thought', a softcore CPU implemented on a FPGA Copyright (C) 2010 Markus Hofstaetter Copyright (C) 2010 Martin Perner Copyright (C) 2010 Stefan Rebernig Copyright (C) 2010 Manfred Schwarz Copyright (C) 2010 Bernhard Urban This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include "disasm.h" #include "ccpu.hpp" #include "CInstrFactory.hpp" #include "uint32_from_hex.hpp" #include "iext.hpp" #include "extensions/cprog.hpp" #include "extensions/cuart.hpp" #include #include "SReadline/SReadline.h" using namespace swift; #define RAM_END (0x3000) #define PROG_END (0x3000) #define REG_COUNT (16) typedef boost::function &)> Func; typedef boost::tuple CompleterElement; typedef list MyCompleterContainer; class LookupFunctor { public: // Creates a functor and memorises tokens LookupFunctor(const vector& tokens) : Tokens(tokens) {} // Compares the first token only bool operator()(const CompleterElement& ele) const { return (strncmp(Tokens.begin()->c_str(), ele.get<0>().c_str(), Tokens.begin()->size()) == 0); } private: const vector &Tokens; }; class CHelpExec { private: const MyCompleterContainer &m_completers; public: CHelpExec(const MyCompleterContainer &cont) : m_completers(cont) {} void operator() (const vector&) { cout << "Available commands: " << endl; for(auto iter = m_completers.begin(); iter != m_completers.end(); ++iter) { cout << setw(19) << setfill(' ') << (*iter).get<0>() << ": " << (*iter).get<2>() << endl; } } }; void close_prog(const std::vector &); CCpu* Iinstr::m_cpu; CCpu* Iext::m_cpu; disasm* Iinstr::m_disasm; disasm* Iext::m_disasm; CCpu* global_cpu = NULL; vector breakpoints; #include multimap dataCommentsStore, progCommentsStore, dataLabelStore, progLabelStore; map dataLineCommentStore, progLineCommentStore; bool ignoreBreak = false; bool exitProg = false; void signalCpuBreak(int) { global_cpu->breakNext(); } void doExit(const vector&) { exitProg = true; } unsigned int convertStringToNum(const std::string& in) { if(in.substr(0,2) == "0x") { return lexical_cast(in); } else { return lexical_cast(in); } } void execStep(const vector& in) { int count = 1; if(in.size() == 2) { try { count = convertStringToNum(in.back()); } catch(bad_cast&) { cerr << "given parameter to step is not a number" << endl; } } while(count > 0) { try { auto breakp = find(breakpoints.begin(), breakpoints.end(), global_cpu->getNextPC()); if(breakp == breakpoints.end() || ignoreBreak) { global_cpu->tick(); ignoreBreak = false; } else { ignoreBreak = true; cout << color(white,red) << "Breakpoint" << color(white,black) << " 0x" << std::hex << setw(8) << setfill('0') << *breakp << std::hex << " hit" << endl; break; } } catch(std::string& e) { cerr << e << endl; } count--; } } void execRun(const vector&) { while(1) { try { auto breakp = find(breakpoints.begin(), breakpoints.end(), global_cpu->getNextPC()); if((breakp == breakpoints.end() || ignoreBreak) && !global_cpu->shouldBreak() ) { global_cpu->tick(); ignoreBreak = false; } else { ignoreBreak = true; cout << color(white,red) << "Breakpoint" << color(white,black) << " 0x" << std::hex << setw(8) << setfill('0') << *breakp << std::hex << " hit" << endl; return; } } catch(std::string& e) { cerr << e << endl; return; } } } void setPC(const vector& in) { CDat addr = 0; if(in.size() == 2) { try { addr = convertStringToNum(in.back()); } catch(bad_cast&) { cerr << "given parameter is not a number" << endl; return; } } global_cpu->setNextPC(addr); cout << "Set programcounter to 0x" << std::hex << setw(8) << setfill('0') << addr << std::dec << endl; } void printReg(const vector& in) { int i, start = 0, end = REG_COUNT-1; /* Todo: * 1) make 2 columns */ if(in.size() >= 2) { try { start = convertStringToNum(in[1]); if(start < 0 || start > (REG_COUNT-1)) { cerr << "start is out of range" << endl; return; } end = start; } catch(bad_cast&) { cerr << "given parameter is not a number" << endl; return; } } if(in.size() >= 3) { try { end = convertStringToNum(in[2]); if(start > end || end > (REG_COUNT-1)) { cerr << "end is out of range or smaller than start" << endl; return; } } catch(bad_cast&) { cerr << "given parameter is not a number" << endl; return; } } for(i = start; i <= end; i++) { cout << setw(2) << setfill('0') << i << ": 0x"; cout << std::hex << setw(8) << setfill('0') << global_cpu->getRegister(i) << " "; cout << std::dec << setw(10) << setfill(' ') << global_cpu->getRegister(i) << " "; cout << std::dec << setw(10) << setfill(' ') << (int)global_cpu->getRegister(i) << endl; } } void setReg(const vector& in) { int reg = 0; CDat val = 0; if(in.size() >= 3) { try { reg = convertStringToNum(in[1]); if(reg < 0 || reg > (REG_COUNT-1)) { cerr << "register is out of range" << endl; return; } val = convertStringToNum(in[2]); cout << "Setting register " << reg << " to 0x" << std::hex << setw(8) << setfill('0') << val << std::dec << endl; global_cpu->setRegister(reg,val); } catch(bad_cast&) { cerr << "given parameter is not a number" << endl; return; } } } void printRAM(const vector& in) { int i, start = 0, end = 15; /* Todo: * 1) make 2 columns */ if(in.size() >= 2) { try { start = convertStringToNum(in[1]); if(start < 0 || start > (RAM_END-1)) { cerr << "start is out of range" << endl; return; } start = (start & (~(BYTE_COUNT-1))) / BYTE_COUNT; end = start; } catch(bad_cast&) { cerr << "given parameter is not a number" << endl; return; } } if(in.size() >= 3) { try { end = convertStringToNum(in[2]); if(start > end || end > (RAM_END-1)) { cerr << "end is out of range or smaller than start" << endl; return; } if(end % BYTE_COUNT != 0) { end = ((end & (~(BYTE_COUNT-1))) / BYTE_COUNT)+1; } else { end = ((end & (~(BYTE_COUNT-1))) / BYTE_COUNT); } } catch(bad_cast&) { cerr << "given parameter is not a number" << endl; return; } } for(i = start*BYTE_COUNT; i <= end*BYTE_COUNT; i += BYTE_COUNT) { { auto range = dataLabelStore.equal_range(i); for(auto iter = range.first; iter != range.second; ++iter) { cout << color(yellow,black) << iter->second << ":" << color(white,black) << endl; } } { auto range = dataCommentsStore.equal_range(i); for(auto iter = range.first; iter != range.second; ++iter) { cout << color(blue,black) << ";" << iter->second << color(white,black) << endl; } } cout << std::hex << "0x" << setw(8) << setfill('0') << i << ": 0x"; cout << std::hex << setw(8) << setfill('0') << global_cpu->getRAM(i) << " "; cout << std::dec << setw(10) << setfill(' ') << global_cpu->getRAM(i) << " "; cout << std::dec << setw(10) << setfill(' ') << (int)global_cpu->getRAM(i); auto iter = dataLineCommentStore.find(i); if(iter != dataLineCommentStore.end()) { cout << color(blue,black) << " ;" << iter->second << color(white,black); } cout << endl; } } void setRam(const vector& in) { int addr = 0; CDat val = 0; if(in.size() >= 3) { try { addr = convertStringToNum(in[1]); if(addr < 0 || addr > (RAM_END-1)) { cerr << "RAM-Address is out of range" << endl; return; } addr = (addr & (~(BYTE_COUNT-1))) / BYTE_COUNT; val = convertStringToNum(in[2]); addr *= BYTE_COUNT; cout << "Setting RAM-Address 0x" << std::hex << setw(8) << setfill('0') << addr; cout << " to 0x" << setw(8) << setfill('0') << val << std::dec << endl; global_cpu->setRAM(addr,val); } catch(bad_cast&) { cerr << "given parameter is not a number" << endl; return; } } } void printPROG(const vector& in) { int i, start = 0, end = 15; /* Todo: * 1) make 2 columns */ if(in.size() >= 2 && in[1][0] == 'c') { start = global_cpu->getCurPC() / BYTE_COUNT; end = start + 9; } else if(in.size() >= 2) { try { start = convertStringToNum(in[1]); if(start < 0 || start > (PROG_END-1)) { cerr << "start is out of range" << endl; return; } start = (start & (~(BYTE_COUNT-1))) / BYTE_COUNT; end = start; } catch(bad_cast&) { cerr << "given parameter is not a number" << endl; return; } } if(in.size() >= 3) { try { end = convertStringToNum(in[2]); if(start > end || end > (PROG_END-1)) { cerr << "end is out of range or smaller than start" << endl; return; } if(end % BYTE_COUNT != 0) { end = ((end & (~(BYTE_COUNT-1))) / BYTE_COUNT)+1; } else { end = ((end & (~(BYTE_COUNT-1))) / BYTE_COUNT); } } catch(bad_cast&) { cerr << "given parameter is not a number" << endl; return; } } for(i = start*BYTE_COUNT; i <= end*BYTE_COUNT; i += BYTE_COUNT) { { auto range = progLabelStore.equal_range(i); for(auto iter = range.first; iter != range.second; ++iter) { cout << color(yellow,black) << iter->second << ":" << color(white,black) << endl; } } { auto range = progCommentsStore.equal_range(i); for(auto iter = range.first; iter != range.second; ++iter) { cout << color(blue,black) << ";" << iter->second << color(white,black) << endl; } } Iinstr* pi = global_cpu->getProg(i); if(pi == NULL) { cout << std::hex << "0x" << setw(8) << setfill('0') << i << ": NOP"; } else { cout << std::hex << "0x" << setw(8) << setfill('0') << i << ": " << std::dec << global_cpu->colorifyInstr(pi->toString()); } auto iter = progLineCommentStore.find(i); if(iter != progLineCommentStore.end()) { cout << color(blue,black) << " ;" << iter->second << color(white,black); } cout << endl; } } void setBreak(const vector& in) { unsigned int addr = 0; if(in.size() == 2) { try { addr = convertStringToNum(in.back()); breakpoints.push_back(addr); cout << "Breakpoint 0x" << std::hex << setw(8) << setfill('0') << addr << std::dec << " set" << endl; } catch(bad_cast&) { cerr << "Given parameter is not a valid address" << endl; } } else { cerr << "Invalid parameter count!" << endl; } } void listBreaks(const vector&) { for(auto iter = breakpoints.begin(); iter != breakpoints.end(); ++iter) { cout << "Breakpoint at 0x" << std::hex << setw(8) << setfill('0') << *iter << std::dec << endl; } } void getPerf(const vector&) { cout << "current perfcounter is " << std::dec << global_cpu->getPerf() << endl; } void resetPerf(const vector&) { cout << "reset perfcounter" << endl; global_cpu->setPerf(0); } void applyToExtensions(const vector& in) { global_cpu->applyToExtensions(in); } void printStatus(const vector&) { CDat stackp = global_cpu->getStack(); CDat stackd = global_cpu->getRAM(stackp); cout << "Stack pointer: 0x" << std::hex << setw(8) << setfill('0') << stackp << " @stackpointer: 0x" << setw(8) << stackd << std::dec << " (" << stackd << ")" << endl; cout << "PSW: 0x" << std::hex << setw(8) << setfill('0') << global_cpu->getFlags() << std::dec << endl; cout << "cur PC: 0x" << std::hex << setw(8) << setfill('0') << global_cpu->getCurPC() << " next PC: 0x" << setw(8) << setfill('0') << global_cpu->getNextPC() << std::dec << endl; } using boost::lexical_cast; using boost::bad_lexical_cast; using namespace std; using namespace boost::program_options; namespace po = boost::program_options; std::string progName; int main(int argc, char* argv[]) { progName = argv[0]; ifstream inFile; try { po::options_description desc("Allowed options"); desc.add_options() ("help,h","produce help message") ("file,f",value(), "input file") ; po::positional_options_description p; p.add("file",1); po::variables_map vm; po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm); po::notify(vm); if(vm.count("help")) { cout << desc << endl; return EXIT_FAILURE; } if(vm.count("file")) { #ifdef DEBUG cout << "going to open file " << vm["file"].as() << endl; #endif inFile.open(vm["file"].as(), ios::in); if(!inFile) { cerr << "Error opening file " << vm["file"].as() << endl; return EXIT_FAILURE; } } else { cout << "not input file given!" << endl << endl; cout << desc << endl; return EXIT_FAILURE; } } catch(std::exception& ex) { cout << ex.what() << endl; } string dir = "./instr/"; map instr; CInstrFactory instrFab; try { instrFab.loadLibsIntoMap(instr, dir); } catch(std::bad_alloc& e) { cerr << progName << ": bad_alloc caught " << e.what() << endl; exit(EXIT_FAILURE); } catch(std::string& s) { cerr << progName << ": " << s << endl; exit(EXIT_FAILURE); } CCpu cpu(REG_COUNT, RAM_END, PROG_END); global_cpu = &cpu; signal(SIGINT, signalCpuBreak); Iinstr::setCPU(&cpu); Iext::setCPU(&cpu); disasm disasm(instr); Iinstr::setDisasm(&disasm); Iext::setDisasm(&disasm); global_cpu->registerExtension(new Cprog()); global_cpu->registerExtension(new Cuart()); vector commentDefer; vector labelDefer; std::string str = ""; int addr = 0; boost::char_separator sep(";", "", boost::keep_empty_tokens); boost::tokenizer > tokens(str, sep); while(getline(inFile, str)) { int count = 0; tokens.assign(str); stringstream out; int type = 0; for(auto tok_iter = tokens.begin(); tok_iter != tokens.end(); ++tok_iter) { if(tok_iter == tokens.begin()) { try { type = lexical_cast(*tok_iter); count++; continue; } catch(bad_lexical_cast &) { break; } cout << endl; } switch(type) { case 0: if(count == 1) { try { addr = lexical_cast(*tok_iter); for(unsigned int i = 0; i < commentDefer.size(); i++) { dataCommentsStore.insert(pair(addr, commentDefer.at(i))); } for(unsigned int i = 0; i < labelDefer.size(); i++) { dataLabelStore.insert(pair(addr, labelDefer.at(i))); } commentDefer.clear(); labelDefer.clear(); } catch(bad_lexical_cast& e) { cerr << e.what() << endl; exit(EXIT_FAILURE); } } else if(count == 2) { try { CDat data = lexical_cast(*tok_iter); cpu.setRAM(addr, data); } catch(bad_lexical_cast& e) { cerr << e.what() << endl; exit(EXIT_FAILURE); } } else if(count == 4) { if((*tok_iter).size() > 0) { dataLabelStore.insert(pair(addr, *tok_iter)); } } else if(count == 5) { if((*tok_iter).size() > 0) { dataLineCommentStore.insert(pair(addr, *tok_iter)); } } break; case 1: if(count == 1) { try { addr = lexical_cast(*tok_iter); for(unsigned int i = 0; i < commentDefer.size(); i++) { progCommentsStore.insert(pair(addr, commentDefer.at(i))); } for(unsigned int i = 0; i < labelDefer.size(); i++) { progLabelStore.insert(pair(addr, labelDefer.at(i))); } commentDefer.clear(); labelDefer.clear(); } catch(bad_lexical_cast& e) { cerr << e.what() << endl; exit(EXIT_FAILURE); } } else if(count == 2) { Iinstr *pi = disasm.decode(*tok_iter); cpu.setProg(addr, pi); } else if(count == 4) { if((*tok_iter).size() > 0) { progLabelStore.insert(pair(addr, *tok_iter)); } } else if(count == 5) { if((*tok_iter).size() > 0) { progLineCommentStore.insert(pair(addr, *tok_iter)); } } break; case 2: if((*tok_iter).size() > 0) { commentDefer.push_back(*tok_iter); } break; case 3: if((*tok_iter).size() > 0) { labelDefer.push_back(*tok_iter); } break; default: cerr << "i was to lazy to implement the other format types for now" << endl; } count++; } } inFile.close(); cout << endl; /* for(int i = 0; i <= 32; i += 4) { Iinstr *pinstr = cpu.getProg(i); if(pinstr != NULL) { cout << i << " : " << std::hex << i << std::dec << " " << pinstr->toString() << endl; } else { cout << "Null at " << i << " : " << std::hex << i << endl; } } for(int i = 0; i <= 32; i += 4) { CDat data = cpu.getRAM(i); cout << i << " : " << std::hex << i << std::dec << " " << data << endl; } */ /* cpu.setRegister(1, 4); cpu.setRegister(2, 0); cpu.setRAM(0,5); cpu.setRAM(4,0x66334455); cpu.setRAM(8,32); cpu.setRAM(12,45); */ // following: job of the bootloader //set stackpointer cpu.setStack(500); //set return to nowhere for ret cpu.setRAM(500,500); SReadline Reader; MyCompleterContainer Completers; CHelpExec HelpExec(Completers); Completers.push_back(CompleterElement("help", boost::bind1st( boost::mem_fun( &CHelpExec::operator()), &HelpExec), "Prints this message")); Completers.push_back(CompleterElement("quit", &doExit, "Exits program")); Completers.push_back(CompleterElement("exit", &doExit, "Exits program")); Completers.push_back(CompleterElement("step [count]",&execStep, "Runs [count] ticks. if count is not given one tick is executed.")); Completers.push_back(CompleterElement("dreg [s] [e]",&printReg, "Prints registers. if s is given, only register s is printed. if s and e are given the registers from s to e are printed. if omitted all registers a printed.")); Completers.push_back(CompleterElement("ddata [s] [e]",&printRAM, "Prints RAM. if s is given, only RAM-Addr. s is printed. if s and e are given the RAM-Addrs from s to e are printed. if omitted the first 16 RAM-Addrs are printed.")); Completers.push_back(CompleterElement("dprog [s] [e]",&printPROG, "Prints program. if s is given, only Prog-Addr. s is printed. if s and e are given the Prog-Addrs from s to e are printed. if omitted the first 16 Prog-Addrs are printed.")); Completers.push_back(CompleterElement("break addr",&setBreak, "Sets a breakpoint for address addr.")); Completers.push_back(CompleterElement("listbreaks",&listBreaks, "Lists all breakpoints.")); Completers.push_back(CompleterElement("run",&execRun, "Runs till next breakpoint or end of program.")); Completers.push_back(CompleterElement("setpc [num]",&setPC, "Sets PC to num. if num is omitted 0 is used.")); Completers.push_back(CompleterElement("setreg [s] [num]",&setReg, "Sets Register s to num.")); Completers.push_back(CompleterElement("setdata [s] [num]",&setRam, "Sets RAM-Addr s to num.")); Completers.push_back(CompleterElement("status",&printStatus, "Prints status of CPU.")); Completers.push_back(CompleterElement("getperf",&getPerf, "Prints performance counter.")); Completers.push_back(CompleterElement("resetperf",&resetPerf, "Resets performance counter to 0.")); Completers.push_back(CompleterElement("extension",&applyToExtensions, "Write to extensions.")); Reader.RegisterCompletions(Completers); string UserInput; vector Tokens, lastTokens; bool EndOfInput = false; //tilab g++44 doesn't like auto here MyCompleterContainer::iterator Found(Completers.end()); Func lastFunc = NULL; while(!exitProg) { UserInput = Reader.GetLine("> ", Tokens, EndOfInput); if(EndOfInput) { break; } if(!Tokens.empty()) { Found = find_if(Completers.begin(), Completers.end(), LookupFunctor(Tokens)); if(Found != Completers.end()) { if((*Found).get<1>() != 0) { lastFunc = (*Found).get<1>(); lastFunc(Tokens); lastTokens = Tokens; //(*Found).get<1>()(Tokens); } } else { lastFunc = NULL; cout << "Unknown command. 'help' displays help" << endl; } } else { if(lastFunc != NULL) { lastFunc(lastTokens); } else { cout << "Unknown command. 'help' displays help" << endl; } } } /* for(int i = 0; ; i++) { try { cpu.tick(); cout << " reg0: " << cpu.getRegister(0) << " reg1: " << cpu.getRegister(1); cout << " reg2: " << cpu.getRegister(2) << " reg3: " << cpu.getRegister(3); cout << " reg4: " << cpu.getRegister(4) << " reg5: " << cpu.getRegister(5); cout << endl << endl; } catch(string& e) { cerr << e << endl; break; } } */ return EXIT_SUCCESS; }