URL
https://opencores.org/ocsvn/lxp32/lxp32/trunk
Subversion Repositories lxp32
Compare Revisions
- This comparison shows the changes necessary to convert path
/lxp32/trunk/tools/src
- from Rev 9 to Rev 6
- ↔ Reverse comparison
Rev 9 → Rev 6
/lxp32asm/assembler.cpp
1,971 → 1,919
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Assembler class. |
*/ |
|
#include "assembler.h" |
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
#include <sstream> |
#include <stdexcept> |
#include <utility> |
#include <limits> |
#include <type_traits> |
#include <cctype> |
#include <cassert> |
#include <cstdlib> |
|
void Assembler::processFile(const std::string &filename) { |
auto nativePath=Utils::normalizeSeparators(filename); |
auto pos=nativePath.find_last_of('/'); |
if(pos!=std::string::npos) nativePath=filename.substr(pos+1); |
_obj.setName(nativePath); |
|
_line=0; |
_state=Initial; |
_currentFileName=filename; |
processFileRecursive(filename); |
|
if(!_currentLabels.empty()) |
throw std::runtime_error("Symbol definition must be followed by an instruction or data definition statement"); |
|
if(!_sectionEnabled.empty()) |
throw std::runtime_error("#endif expected"); |
|
// Examine symbol table |
for(auto const &sym: _obj.symbols()) { |
if(sym.second.type==LinkableObject::Unknown&&!sym.second.refs.empty()) { |
std::ostringstream msg; |
msg<<"Undefined symbol \""+sym.first+"\""; |
msg<<" (referenced from "<<sym.second.refs[0].source; |
msg<<":"<<sym.second.refs[0].line<<")"; |
throw std::runtime_error(msg.str()); |
} |
} |
|
for(auto const &sym: _exportedSymbols) _obj.exportSymbol(sym); |
} |
|
void Assembler::processFileRecursive(const std::string &filename) { |
std::ifstream in(filename,std::ios_base::in); |
if(!in) throw std::runtime_error("Cannot open file \""+filename+"\""); |
|
// Process input file line-by-line |
auto savedLine=_line; |
auto savedState=_state; |
auto savedFileName=_currentFileName; |
|
_line=1; |
_state=Initial; |
_currentFileName=filename; |
|
std::string line; |
while(std::getline(in,line)) { |
auto tokens=tokenize(line); |
expand(tokens); |
elaborate(tokens); |
_line++; |
} |
|
if(_state!=Initial) throw std::runtime_error("Unexpected end of file"); |
|
_line=savedLine; |
_state=savedState; |
_currentFileName=savedFileName; |
} |
|
void Assembler::addIncludeSearchDir(const std::string &dir) { |
auto ndir=Utils::normalizeSeparators(dir); |
if(!ndir.empty()&&ndir.back()!='/') ndir.push_back('/'); |
_includeSearchDirs.push_back(std::move(ndir)); |
} |
|
int Assembler::line() const { |
return _line; |
} |
|
std::string Assembler::currentFileName() const { |
return _currentFileName; |
} |
|
LinkableObject &Assembler::object() { |
return _obj; |
} |
|
const LinkableObject &Assembler::object() const { |
return _obj; |
} |
|
Assembler::TokenList Assembler::tokenize(const std::string &str) { |
TokenList tokenList; |
std::string word; |
std::size_t i; |
for(i=0;i<str.size();i++) { |
char ch=str[i]; |
switch(_state) { |
case Initial: |
if(ch==' '||ch=='\t'||ch=='\n'||ch=='\r') continue; // skip whitespace |
else if(ch==','||ch==':') { // separator |
tokenList.push_back(std::string(1,ch)); |
} |
else if(std::isalnum(ch)||ch=='.'||ch=='#'||ch=='_'||ch=='-'||ch=='+') { |
word=std::string(1,ch); |
_state=Word; |
} |
else if(ch=='\"') { |
word="\""; |
_state=StringLiteral; |
} |
else if(ch=='/') { |
if(++i>=str.size()) throw std::runtime_error("Unexpected end of line"); |
ch=str[i]; |
if(ch=='/') i=str.size(); // skip the rest of the line |
else if(ch=='*') _state=BlockComment; |
else throw std::runtime_error(std::string("Unexpected character: \"")+ch+"\""); |
} |
else throw std::runtime_error(std::string("Unexpected character: \"")+ch+"\""); |
break; |
case Word: |
if(std::isalnum(ch)||ch=='_'||ch=='@'||ch=='+'||ch=='-') word+=ch; |
else { |
i--; |
_state=Initial; |
tokenList.push_back(std::move(word)); |
} |
break; |
case StringLiteral: |
if(ch=='\\') { |
if(++i>=str.size()) throw std::runtime_error("Unexpected end of line"); |
ch=str[i]; |
if(ch=='\\') word.push_back('\\'); |
else if(ch=='\"') word.push_back('\"'); |
else if(ch=='\'') word.push_back('\''); |
else if(ch=='t') word.push_back('\t'); |
else if(ch=='n') word.push_back('\n'); |
else if(ch=='r') word.push_back('\r'); |
else if(ch=='x') { // hexadecimal sequence can be 1-2 digit long |
std::string seq; |
if(i+1<str.size()&&Utils::ishexdigit(str[i+1])) seq+=str[i+1]; |
if(i+2<str.size()&&Utils::ishexdigit(str[i+2])) seq+=str[i+2]; |
if(seq.empty()) throw std::runtime_error("Ill-formed escape sequence"); |
try { |
word.push_back(static_cast<char>(std::stoul(seq,nullptr,16))); |
} |
catch(std::exception &) { |
throw std::runtime_error("Ill-formed escape sequence"); |
} |
i+=seq.size(); |
} |
else if(Utils::isoctdigit(ch)) { // octal sequence can be 1-3 digit long |
std::string seq(1,ch); |
if(i+1<str.size()&&Utils::isoctdigit(str[i+1])) seq+=str[i+1]; |
if(i+2<str.size()&&Utils::isoctdigit(str[i+2])) seq+=str[i+2]; |
unsigned long value; |
try { |
value=std::stoul(seq,nullptr,8); |
} |
catch(std::exception &) { |
throw std::runtime_error("Ill-formed escape sequence"); |
} |
|
if(value>255) throw std::runtime_error("Octal value is out of range"); |
word.push_back(static_cast<char>(value)); |
|
i+=seq.size()-1; |
} |
else throw std::runtime_error(std::string("Unknown escape sequence: \"\\")+ch+"\""); |
} |
else if(ch=='\"') { |
word.push_back('\"'); |
tokenList.push_back(std::move(word)); |
_state=Initial; |
} |
else word.push_back(ch); |
break; |
case BlockComment: |
if(ch=='*') { |
if(++i>=str.size()) break; |
ch=str[i]; |
if(ch=='/') _state=Initial; |
else i--; |
} |
break; |
} |
} |
|
if(_state==StringLiteral) throw std::runtime_error("Unexpected end of line"); |
if(_state==Word) tokenList.push_back(std::move(word)); // store last word |
if(_state!=BlockComment) _state=Initial; // reset state if not in block comment |
|
return tokenList; |
} |
|
void Assembler::expand(TokenList &list) { |
TokenList newlist; |
// Perform macro substitution |
for(auto &token: list) { |
auto it=_macros.find(token); |
bool substitute=false; |
if(it!=_macros.end()) { |
substitute=true; |
// Don't substitute macros for a second token in certain directives |
if(newlist.size()==1) { |
if(newlist[0]=="#define") substitute=false; |
else if(newlist[0]=="#ifdef") substitute=false; |
else if(newlist[0]=="#ifndef") substitute=false; |
} |
else if(newlist.size()==3&&newlist[1]==":") { |
if(newlist[2]=="#define") substitute=false; |
else if(newlist[2]=="#ifdef") substitute=false; |
else if(newlist[2]=="#ifndef") substitute=false; |
} |
} |
|
if(substitute) { |
for(auto const &replace: it->second) newlist.push_back(replace); |
} |
else newlist.push_back(std::move(token)); |
} |
list=std::move(newlist); |
} |
|
void Assembler::elaborate(TokenList &list) { |
if(list.empty()) return; |
|
// Process label (if present) |
if(list.size()>=2&&list[1]==":") { |
if(!validateIdentifier(list[0])) |
throw std::runtime_error("Ill-formed identifier: \""+list[0]+"\""); |
if(isSectionEnabled()) _currentLabels.push_back(std::move(list[0])); |
list.erase(list.begin(),list.begin()+2); |
} |
|
if(list.empty()) return; |
|
// If the section is disabled, we look only for #ifdef, #ifndef, #else or #endif |
if(!isSectionEnabled()&&list[0]!="#ifdef"&&list[0]!="#ifndef"&& |
list[0]!="#else"&&list[0]!="#endif") return; |
|
// Process statement itself |
if(list[0][0]=='#') elaborateDirective(list); |
else { |
LinkableObject::Word rva; |
if(list[0][0]=='.') rva=elaborateDataDefinition(list); |
else rva=elaborateInstruction(list); |
|
for(auto const &label: _currentLabels) { |
_obj.addSymbol(label,rva); |
} |
_currentLabels.clear(); |
} |
} |
|
void Assembler::elaborateDirective(TokenList &list) { |
assert(!list.empty()); |
|
if(list[0]=="#define") { |
if(list.size()<2) |
throw std::runtime_error("Wrong number of tokens in the directive"); |
if(_macros.find(list[1])!=_macros.end()) |
throw std::runtime_error("Macro \""+list[1]+"\" has been already defined"); |
if(!validateIdentifier(list[1])) |
throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\""); |
_macros.emplace(list[1],TokenList(list.begin()+2,list.end())); |
} |
else if(list[0]=="#export") { |
if(list.size()!=2) throw std::runtime_error("Wrong number of tokens in the directive"); |
if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\""); |
_exportedSymbols.push_back(list[1]); |
} |
else if(list[0]=="#import") { |
if(list.size()!=2) throw std::runtime_error("Wrong number of tokens in the directive"); |
if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\""); |
_obj.addImportedSymbol(list[1]); |
} |
else if(list[0]=="#include") { |
if(list.size()!=2) throw std::runtime_error("Wrong number of tokens in the directive"); |
auto filename=Utils::dequoteString(list[1]); |
if(Utils::isAbsolutePath(filename)) return processFileRecursive(filename); |
else { |
auto path=Utils::relativePath(currentFileName(),filename); |
if(Utils::fileExists(path)) return processFileRecursive(path); |
else { |
for(auto const &dir: _includeSearchDirs) { |
path=Utils::nativeSeparators(dir+filename); |
if(Utils::fileExists(path)) return processFileRecursive(path); |
} |
} |
} |
throw std::runtime_error("Cannot locate include file \""+filename+"\""); |
} |
else if(list[0]=="#message") { |
if(list.size()!=2) throw std::runtime_error("Wrong number of tokens in the directive"); |
auto msg=Utils::dequoteString(list[1]); |
std::cout<<currentFileName()<<":"<<line()<<": "<<msg<<std::endl; |
} |
else if(list[0]=="#error") { |
if(list.size()<2) throw std::runtime_error("#error directive encountered"); |
auto msg=Utils::dequoteString(list[1]); |
throw std::runtime_error(msg); |
} |
else if(list[0]=="#ifdef") { |
if(list.size()!=2) throw std::runtime_error("Wrong number of tokens in the directive"); |
if(_macros.find(list[1])!=_macros.end()) _sectionEnabled.push_back(true); |
else _sectionEnabled.push_back(false); |
} |
else if(list[0]=="#ifndef") { |
if(list.size()!=2) throw std::runtime_error("Wrong number of tokens in the directive"); |
if(_macros.find(list[1])!=_macros.end()) _sectionEnabled.push_back(false); |
else _sectionEnabled.push_back(true); |
} |
else if(list[0]=="#else") { |
if(list.size()!=1) throw std::runtime_error("Wrong number of tokens in the directive"); |
if(_sectionEnabled.empty()) throw std::runtime_error("Unexpected #else"); |
_sectionEnabled.back()=!_sectionEnabled.back(); |
} |
else if(list[0]=="#endif") { |
if(list.size()!=1) throw std::runtime_error("Wrong number of tokens in the directive"); |
if(_sectionEnabled.empty()) throw std::runtime_error("Unexpected #endif"); |
_sectionEnabled.pop_back(); |
} |
else throw std::runtime_error("Unrecognized directive: \""+list[0]+"\""); |
} |
|
LinkableObject::Word Assembler::elaborateDataDefinition(TokenList &list) { |
assert(!list.empty()); |
|
LinkableObject::Word rva=0; |
|
if(list[0]==".align") { |
if(list.size()>2) throw std::runtime_error("Unexpected token: \""+list[2]+"\""); |
std::size_t align=4; |
if(list.size()>1) align=static_cast<std::size_t>(numericLiteral(list[1])); |
if(!Utils::isPowerOf2(align)) throw std::runtime_error("Alignment must be a power of 2"); |
if(align<4) throw std::runtime_error("Alignment must be at least 4"); |
rva=_obj.addPadding(align); |
} |
else if(list[0]==".reserve") { |
if(list.size()<2) throw std::runtime_error("Unexpected end of statement"); |
else if(list.size()>2) throw std::runtime_error("Unexpected token: \""+list[2]+"\""); |
auto n=static_cast<std::size_t>(numericLiteral(list[1])); |
rva=_obj.addZeros(n); |
} |
else if(list[0]==".word") { |
if(list.size()<2) throw std::runtime_error("Unexpected end of statement"); |
for(std::size_t i=1;i<list.size();i++) { |
if(i%2!=0) { |
auto w=static_cast<LinkableObject::Word>(numericLiteral(list[i])); |
auto r=_obj.addWord(w); |
if(i==1) rva=r; |
} |
else { |
if(list[i]!=",") throw std::runtime_error("Comma expected"); |
if(i+1==list.size()) throw std::runtime_error("Unexpected end of statement"); |
} |
} |
} |
else if(list[0]==".byte") { |
if(list.size()<2) throw std::runtime_error("Unexpected end of statement"); |
for(std::size_t i=1;i<list.size();i++) { |
if(i%2!=0) { |
if(list[i].at(0)=='\"') { // string literal |
auto bytes=Utils::dequoteString(list[i]); |
auto r=_obj.addBytes(reinterpret_cast<const LinkableObject::Byte*> |
(bytes.c_str()),bytes.size()); |
if(i==1) rva=r; |
} |
else { |
auto n=numericLiteral(list[i]); |
|
if(n>255||n<-128) throw std::runtime_error("\""+list[i]+"\": out of range"); |
|
auto b=static_cast<LinkableObject::Byte>(n); |
auto r=_obj.addByte(b); |
if(i==1) rva=r; |
} |
} |
else { |
if(list[i]!=",") throw std::runtime_error("Comma expected"); |
if(i+1==list.size()) throw std::runtime_error("Unexpected end of statement"); |
} |
} |
} |
else throw std::runtime_error("Unrecognized statement: \""+list[0]+"\""); |
|
return rva; |
} |
|
LinkableObject::Word Assembler::elaborateInstruction(TokenList &list) { |
assert(!list.empty()); |
auto rva=_obj.addPadding(); |
if(list[0]=="add") encodeAdd(list); |
else if(list[0]=="and") encodeAnd(list); |
else if(list[0]=="call") encodeCall(list); |
else if(list[0].substr(0,4)=="cjmp") encodeCjmpxx(list); |
else if(list[0]=="divs") encodeDivs(list); |
else if(list[0]=="divu") encodeDivu(list); |
else if(list[0]=="hlt") encodeHlt(list); |
else if(list[0]=="jmp") encodeJmp(list); |
else if(list[0]=="iret") encodeIret(list); |
else if(list[0]=="lc") encodeLc(list); |
else if(list[0]=="lcs") encodeLcs(list); |
else if(list[0]=="lsb") encodeLsb(list); |
else if(list[0]=="lub") encodeLub(list); |
else if(list[0]=="lw") encodeLw(list); |
else if(list[0]=="mods") encodeMods(list); |
else if(list[0]=="modu") encodeModu(list); |
else if(list[0]=="mov") encodeMov(list); |
else if(list[0]=="mul") encodeMul(list); |
else if(list[0]=="neg") encodeNeg(list); |
else if(list[0]=="nop") encodeNop(list); |
else if(list[0]=="not") encodeNot(list); |
else if(list[0]=="or") encodeOr(list); |
else if(list[0]=="ret") encodeRet(list); |
else if(list[0]=="sb") encodeSb(list); |
else if(list[0]=="sl") encodeSl(list); |
else if(list[0]=="srs") encodeSrs(list); |
else if(list[0]=="sru") encodeSru(list); |
else if(list[0]=="sub") encodeSub(list); |
else if(list[0]=="sw") encodeSw(list); |
else if(list[0]=="xor") encodeXor(list); |
else throw std::runtime_error("Unrecognized instruction: \""+list[0]+"\""); |
return rva; |
} |
|
bool Assembler::isSectionEnabled() const { |
if(_sectionEnabled.empty()) return true; |
bool enabled=true; |
for(auto b: _sectionEnabled) enabled=enabled&&b; |
return enabled; |
} |
|
bool Assembler::validateIdentifier(const std::string &str) { |
/* |
* Valid identifier must satisfy the following requirements: |
* 1. Must not be empty |
* 2. The first character must be either alphabetic or an underscore |
* 3. Subsequent characters must be either alphanumeric or underscores |
*/ |
if(str.empty()) return false; |
for(std::size_t i=0;i<str.size();i++) { |
char ch=str[i]; |
if(i==0) { |
if(!std::isalpha(ch)&&ch!='_') return false; |
} |
else { |
if(!std::isalnum(ch)&&ch!='_') return false; |
} |
} |
return true; |
} |
|
Assembler::Integer Assembler::numericLiteral(const std::string &str) { |
std::size_t pos; |
Integer i; |
try { |
i=std::stoll(str,&pos,0); |
} |
catch(std::exception &) { |
throw std::runtime_error("Ill-formed numeric literal: \""+str+"\""); |
} |
if(pos<str.size()) throw std::runtime_error("Ill-formed numeric literal: \""+str+"\""); |
|
typedef std::make_signed<LinkableObject::Word>::type SignedWord; |
|
if(i>static_cast<Integer>(std::numeric_limits<LinkableObject::Word>::max())|| |
i<static_cast<Integer>(std::numeric_limits<SignedWord>::min())) |
throw std::runtime_error("\""+str+"\": out of range"); |
|
return i; |
} |
|
std::vector<Assembler::Operand> Assembler::getOperands(const TokenList &list) { |
std::vector<Operand> arglist; |
for(std::size_t i=1;i<list.size();i++) { |
if(i%2!=0) { |
Operand a; |
a.str=list[i]; |
|
if(!list[i].empty()&&list[i][0]=='r') { |
// Is argument a register? |
char *endptr; |
auto regstr=list[i].substr(1); |
auto reg=std::strtol(regstr.c_str(),&endptr,10); |
|
if(!*endptr&®>=0&®<=255) { |
a.type=Operand::Register; |
a.reg=static_cast<std::uint8_t>(reg); |
arglist.push_back(std::move(a)); |
continue; |
} |
} |
|
// Try alternative register names |
if(list[i]=="sp") { // stack pointer |
a.type=Operand::Register; |
a.reg=255; |
arglist.push_back(std::move(a)); |
} |
else if(list[i]=="rp") { // return pointer |
a.type=Operand::Register; |
a.reg=254; |
arglist.push_back(std::move(a)); |
} |
else if(list[i]=="irp") { // interrupt return pointer |
a.type=Operand::Register; |
a.reg=253; |
arglist.push_back(std::move(a)); |
} |
else if(list[i]=="cr") { // control register |
a.type=Operand::Register; |
a.reg=252; |
arglist.push_back(std::move(a)); |
} |
else if(list[i].size()==3&&list[i].substr(0,2)=="iv"&& |
list[i][2]>='0'&&list[i][2]<='7') // interrupt vector |
{ |
a.type=Operand::Register; |
a.reg=240+(list[i][2]-'0'); |
arglist.push_back(std::move(a)); |
} |
else if(validateIdentifier(list[i])) { |
// Is argument an identifier? |
a.type=Operand::Identifier; |
arglist.push_back(std::move(a)); |
} |
else { |
auto atpos=list[i].find_first_of('@'); |
if(atpos!=std::string::npos) { |
// Identifier with an offset? |
a.type=Operand::Identifier; |
a.str=list[i].substr(0,atpos); |
if(!validateIdentifier(a.str)) throw std::runtime_error("Ill-formed identifier"); |
a.i=numericLiteral(list[i].substr(atpos+1)); |
arglist.push_back(std::move(a)); |
} |
else { |
// Numeric literal? |
a.type=Operand::NumericLiteral; |
a.i=numericLiteral(list[i]); |
arglist.push_back(std::move(a)); |
} |
} |
} |
else { |
if(list[i]!=",") throw std::runtime_error("Comma expected"); |
if(i+1==list.size()) throw std::runtime_error("Unexpected end of line"); |
} |
} |
return arglist; |
} |
|
/* |
* Member functions to encode LXP32 instructions |
*/ |
|
void Assembler::encodeDstOperand(LinkableObject::Word &word,const Operand &arg) { |
if(arg.type!=Operand::Register) |
throw std::runtime_error("\""+arg.str+"\": must be a register"); |
word|=arg.reg<<16; |
} |
|
void Assembler::encodeRd1Operand(LinkableObject::Word &word,const Operand &arg) { |
if(arg.type==Operand::Register) { |
word|=0x02000000; |
word|=arg.reg<<8; |
} |
else if(arg.type==Operand::NumericLiteral) { |
if((arg.i<-128||arg.i>127)&&(arg.i<0xFFFFFF80||arg.i>0xFFFFFFFF)) |
throw std::runtime_error("\""+arg.str+"\": out of range"); |
auto b=static_cast<LinkableObject::Byte>(arg.i); |
word|=b<<8; |
} |
else throw std::runtime_error("\""+arg.str+"\": bad argument"); |
} |
|
void Assembler::encodeRd2Operand(LinkableObject::Word &word,const Operand &arg) { |
if(arg.type==Operand::Register) { |
word|=0x01000000; |
word|=arg.reg; |
} |
else if(arg.type==Operand::NumericLiteral) { |
if((arg.i<-128||arg.i>127)&&(arg.i<0xFFFFFF80||arg.i>0xFFFFFFFF)) |
throw std::runtime_error("\""+arg.str+"\": out of range"); |
auto b=static_cast<LinkableObject::Byte>(arg.i); |
word|=b; |
} |
else throw std::runtime_error("\""+arg.str+"\": bad argument"); |
} |
|
void Assembler::encodeAdd(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("add instruction requires 3 operands"); |
LinkableObject::Word w=0x40000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeAnd(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("and instruction requires 3 operands"); |
LinkableObject::Word w=0x60000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeCall(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=1) throw std::runtime_error("call instruction requires 1 operand"); |
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register"); |
LinkableObject::Word w=0x86FE0000; |
encodeRd1Operand(w,args[0]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeCjmpxx(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("cjmpxx instruction requires 3 operands"); |
|
LinkableObject::Word w; |
bool reverse=false; |
/* |
* Note: cjmpul, cjmpule, cjmpsl and cjmpsle don't have distinct opcodes; |
* instead, they are aliases for respective "g" or "ge" instructions |
* with reversed operand order. |
*/ |
if(list[0]=="cjmpe") w=0xE0000000; |
else if(list[0]=="cjmpne") w=0xD0000000; |
else if(list[0]=="cjmpug"||list[0]=="cjmpul") w=0xC8000000; |
else if(list[0]=="cjmpuge"||list[0]=="cjmpule") w=0xE8000000; |
else if(list[0]=="cjmpsg"||list[0]=="cjmpsl") w=0xC4000000; |
else if(list[0]=="cjmpsge"||list[0]=="cjmpsle") w=0xE4000000; |
else throw std::runtime_error("Unrecognized instruction: \""+list[0]+"\""); |
|
if(list[0]=="cjmpul"||list[0]=="cjmpule"|| |
list[0]=="cjmpsl"||list[0]=="cjmpsle") reverse=true; |
|
encodeDstOperand(w,args[0]); |
|
if(!reverse) { |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
} |
else { |
encodeRd1Operand(w,args[2]); |
encodeRd2Operand(w,args[1]); |
} |
_obj.addWord(w); |
} |
|
void Assembler::encodeDivs(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("divs instruction requires 3 operands"); |
LinkableObject::Word w=0x54000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeDivu(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("divu instruction requires 3 operands"); |
LinkableObject::Word w=0x50000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeHlt(const TokenList &list) { |
auto args=getOperands(list); |
if(!args.empty()) throw std::runtime_error("hlt instruction doesn't take operands"); |
_obj.addWord(0x08000000); |
} |
|
void Assembler::encodeJmp(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=1) throw std::runtime_error("jmp instruction requires 1 operand"); |
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register"); |
LinkableObject::Word w=0x82000000; |
encodeRd1Operand(w,args[0]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeIret(const TokenList &list) { |
// Note: "iret" is not a real instruction, but an alias for "jmp irp" |
auto args=getOperands(list); |
if(!args.empty()) throw std::runtime_error("iret instruction doesn't take operands"); |
_obj.addWord(0x8200FD00); |
} |
|
void Assembler::encodeLc(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lc instruction requires 2 operands"); |
|
LinkableObject::Word w=0x04000000; |
encodeDstOperand(w,args[0]); |
_obj.addWord(w); |
|
if(args[1].type==Operand::Identifier) { |
LinkableObject::Reference ref; |
ref.source=currentFileName(); |
ref.line=line(); |
ref.rva=_obj.addWord(0); |
ref.offset=args[1].i; |
ref.type=LinkableObject::Regular; |
_obj.addReference(args[1].str,ref); |
} |
else if(args[1].type==Operand::NumericLiteral) { |
_obj.addWord(static_cast<LinkableObject::Word>(args[1].i)); |
} |
else throw std::runtime_error("\""+args[1].str+"\": bad argument"); |
} |
|
void Assembler::encodeLcs(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lcs instruction requires 2 operands"); |
|
LinkableObject::Word w=0xA0000000; |
encodeDstOperand(w,args[0]); |
|
if(args[1].type==Operand::NumericLiteral) { |
if((args[1].i<-1048576||args[1].i>1048575)&&(args[1].i<0xFFF00000||args[1].i>0xFFFFFFFF)) |
throw std::runtime_error("\""+args[1].str+"\": out of range"); |
auto c=static_cast<LinkableObject::Word>(args[1].i)&0x1FFFFF; |
w|=(c&0xFFFF); |
w|=((c<<8)&0x1F000000); |
_obj.addWord(w); |
} |
else if(args[1].type==Operand::Identifier) { |
LinkableObject::Reference ref; |
ref.source=currentFileName(); |
ref.line=line(); |
ref.rva=_obj.addWord(w); |
ref.offset=args[1].i; |
ref.type=LinkableObject::Short; |
_obj.addReference(args[1].str,ref); |
} |
else throw std::runtime_error("\""+args[1].str+"\": bad argument"); |
} |
|
void Assembler::encodeLsb(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lsb instruction requires 2 operands"); |
if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register"); |
LinkableObject::Word w=0x2E000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeLub(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lub instruction requires 2 operands"); |
if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register"); |
LinkableObject::Word w=0x2A000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeLw(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lw instruction requires 2 operands"); |
if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register"); |
LinkableObject::Word w=0x22000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeMods(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("mods instruction requires 3 operands"); |
LinkableObject::Word w=0x5C000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeModu(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("modu instruction requires 3 operands"); |
LinkableObject::Word w=0x58000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeMov(const TokenList &list) { |
// Note: "mov" is not a real instruction, but an alias for "add dst, src, 0" |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("mov instruction requires 2 operands"); |
LinkableObject::Word w=0x40000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeMul(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("mul instruction requires 3 operands"); |
LinkableObject::Word w=0x48000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeNeg(const TokenList &list) { |
// Note: "neg" is not a real instruction, but an alias for "sub dst, 0, src" |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("neg instruction requires 2 operands"); |
LinkableObject::Word w=0x44000000; |
encodeDstOperand(w,args[0]); |
encodeRd2Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeNop(const TokenList &list) { |
auto args=getOperands(list); |
if(!args.empty()) throw std::runtime_error("nop instruction doesn't take operands"); |
_obj.addWord(0); |
} |
|
void Assembler::encodeNot(const TokenList &list) { |
// Note: "not" is not a real instruction, but an alias for "xor dst, src, -1" |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("not instruction requires 2 operands"); |
LinkableObject::Word w=0x680000FF; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeOr(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("or instruction requires 3 operands"); |
LinkableObject::Word w=0x64000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeRet(const TokenList &list) { |
// Note: "ret" is not a real instruction, but an alias for "jmp rp" |
auto args=getOperands(list); |
if(!args.empty()) throw std::runtime_error("ret instruction doesn't take operands"); |
_obj.addWord(0x8200FE00); |
} |
|
void Assembler::encodeSb(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("sb instruction requires 2 operands"); |
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register"); |
if(args[1].type==Operand::NumericLiteral) { |
// If numeric literal value is between 128 and 255 (inclusive), convert |
// it to a signed byte to avoid exception in encodeRd2Operand() |
if(args[1].i>=128&&args[1].i<=255) args[1].i-=256; |
} |
LinkableObject::Word w=0x3A000000; |
encodeRd1Operand(w,args[0]); |
encodeRd2Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSl(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("sl instruction requires 3 operands"); |
if(args[2].type==Operand::NumericLiteral&& |
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word)))) |
{ |
std::cerr<<currentFileName()<<":"<<line()<<": "; |
std::cerr<<"Warning: Bitwise shift result is undefined when " |
"the second operand is negative or greater than 31"<<std::endl; |
} |
|
LinkableObject::Word w=0x70000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSrs(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("srs instruction requires 3 operands"); |
if(args[2].type==Operand::NumericLiteral&& |
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word)))) |
{ |
std::cerr<<currentFileName()<<":"<<line()<<": "; |
std::cerr<<"Warning: Bitwise shift result is undefined when " |
"the second operand is negative or greater than 31"<<std::endl; |
} |
|
LinkableObject::Word w=0x7C000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSru(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("sru instruction requires 3 operands"); |
if(args[2].type==Operand::NumericLiteral&& |
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word)))) |
{ |
std::cerr<<currentFileName()<<":"<<line()<<": "; |
std::cerr<<"Warning: Bitwise shift result is undefined when " |
"the second operand is negative or greater than 31"<<std::endl; |
} |
|
LinkableObject::Word w=0x78000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSub(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("sub instruction requires 3 operands"); |
LinkableObject::Word w=0x44000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSw(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("sw instruction requires 2 operands"); |
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register"); |
LinkableObject::Word w=0x32000000; |
encodeRd1Operand(w,args[0]); |
encodeRd2Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeXor(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("xor instruction requires 3 operands"); |
LinkableObject::Word w=0x68000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Assembler class. |
*/ |
|
#include "assembler.h" |
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
#include <sstream> |
#include <stdexcept> |
#include <utility> |
#include <limits> |
#include <type_traits> |
#include <cctype> |
#include <cassert> |
#include <cstdlib> |
|
void Assembler::processFile(const std::string &filename) { |
auto nativePath=Utils::normalizeSeparators(filename); |
auto pos=nativePath.find_last_of('/'); |
if(pos!=std::string::npos) nativePath=filename.substr(pos+1); |
_obj.setName(nativePath); |
|
_line=0; |
_state=Initial; |
_currentFileName=filename; |
processFileRecursive(filename); |
|
// Examine symbol table |
for(auto const &sym: _obj.symbols()) { |
if(sym.second.type==LinkableObject::Unknown&&!sym.second.refs.empty()) { |
std::ostringstream msg; |
msg<<"Undefined symbol \""+sym.first+"\""; |
msg<<" (referenced from "<<sym.second.refs[0].source; |
msg<<":"<<sym.second.refs[0].line<<")"; |
throw std::runtime_error(msg.str()); |
} |
} |
|
for(auto const &sym: _exportedSymbols) _obj.exportSymbol(sym); |
} |
|
void Assembler::processFileRecursive(const std::string &filename) { |
std::ifstream in(filename,std::ios_base::in); |
if(!in) throw std::runtime_error("Cannot open file \""+filename+"\""); |
|
// Process input file line-by-line |
auto savedLine=_line; |
auto savedState=_state; |
auto savedFileName=_currentFileName; |
|
_line=1; |
_state=Initial; |
_currentFileName=filename; |
|
std::string line; |
while(std::getline(in,line)) { |
auto tokens=tokenize(line); |
expand(tokens); |
elaborate(tokens); |
_line++; |
} |
|
if(_state!=Initial) throw std::runtime_error("Unexpected end of file"); |
|
_line=savedLine; |
_state=savedState; |
_currentFileName=savedFileName; |
|
if(!_currentLabels.empty()) |
throw std::runtime_error("Symbol definition must be followed by an instruction or data definition statement"); |
} |
|
void Assembler::addIncludeSearchDir(const std::string &dir) { |
auto ndir=Utils::normalizeSeparators(dir); |
if(!ndir.empty()&&ndir.back()!='/') ndir.push_back('/'); |
_includeSearchDirs.push_back(std::move(ndir)); |
} |
|
int Assembler::line() const { |
return _line; |
} |
|
std::string Assembler::currentFileName() const { |
return _currentFileName; |
} |
|
LinkableObject &Assembler::object() { |
return _obj; |
} |
|
const LinkableObject &Assembler::object() const { |
return _obj; |
} |
|
Assembler::TokenList Assembler::tokenize(const std::string &str) { |
TokenList tokenList; |
std::string word; |
std::size_t i; |
for(i=0;i<str.size();i++) { |
char ch=str[i]; |
switch(_state) { |
case Initial: |
if(ch==' '||ch=='\t'||ch=='\n'||ch=='\r') continue; // skip whitespace |
else if(ch==','||ch==':') { // separator |
tokenList.push_back(std::string(1,ch)); |
} |
else if(std::isalnum(ch)||ch=='.'||ch=='#'||ch=='_'||ch=='-'||ch=='+') { |
word=std::string(1,ch); |
_state=Word; |
} |
else if(ch=='\"') { |
word="\""; |
_state=StringLiteral; |
} |
else if(ch=='/') { |
if(++i>=str.size()) throw std::runtime_error("Unexpected end of line"); |
ch=str[i]; |
if(ch=='/') i=str.size(); // skip the rest of the line |
else if(ch=='*') _state=BlockComment; |
else throw std::runtime_error(std::string("Unexpected character: \"")+ch+"\""); |
} |
else throw std::runtime_error(std::string("Unexpected character: \"")+ch+"\""); |
break; |
case Word: |
if(std::isalnum(ch)||ch=='_'||ch=='@'||ch=='+'||ch=='-') word+=ch; |
else { |
i--; |
_state=Initial; |
tokenList.push_back(std::move(word)); |
} |
break; |
case StringLiteral: |
if(ch=='\\') { |
if(++i>=str.size()) throw std::runtime_error("Unexpected end of line"); |
ch=str[i]; |
if(ch=='\\') word.push_back('\\'); |
else if(ch=='\"') word.push_back('\"'); |
else if(ch=='\'') word.push_back('\''); |
else if(ch=='t') word.push_back('\t'); |
else if(ch=='n') word.push_back('\n'); |
else if(ch=='r') word.push_back('\r'); |
else if(ch=='x') { // hexadecimal sequence can be 1-2 digit long |
std::string seq; |
if(i+1<str.size()&&Utils::ishexdigit(str[i+1])) seq+=str[i+1]; |
if(i+2<str.size()&&Utils::ishexdigit(str[i+2])) seq+=str[i+2]; |
if(seq.empty()) throw std::runtime_error("Ill-formed escape sequence"); |
try { |
word.push_back(static_cast<char>(std::stoul(seq,nullptr,16))); |
} |
catch(std::exception &) { |
throw std::runtime_error("Ill-formed escape sequence"); |
} |
i+=seq.size(); |
} |
else if(Utils::isoctdigit(ch)) { // octal sequence can be 1-3 digit long |
std::string seq(1,ch); |
if(i+1<str.size()&&Utils::isoctdigit(str[i+1])) seq+=str[i+1]; |
if(i+2<str.size()&&Utils::isoctdigit(str[i+2])) seq+=str[i+2]; |
unsigned long value; |
try { |
value=std::stoul(seq,nullptr,8); |
} |
catch(std::exception &) { |
throw std::runtime_error("Ill-formed escape sequence"); |
} |
|
if(value>255) throw std::runtime_error("Octal value is out of range"); |
word.push_back(static_cast<char>(value)); |
|
i+=seq.size()-1; |
} |
else throw std::runtime_error(std::string("Unknown escape sequence: \"\\")+ch+"\""); |
} |
else if(ch=='\"') { |
word.push_back('\"'); |
tokenList.push_back(std::move(word)); |
_state=Initial; |
} |
else word.push_back(ch); |
break; |
case BlockComment: |
if(ch=='*') { |
if(++i>=str.size()) break; |
ch=str[i]; |
if(ch=='/') _state=Initial; |
else i--; |
} |
break; |
} |
} |
|
if(_state==StringLiteral) throw std::runtime_error("Unexpected end of line"); |
if(_state==Word) tokenList.push_back(std::move(word)); // store last word |
if(_state!=BlockComment) _state=Initial; // reset state if not in block comment |
|
return tokenList; |
} |
|
void Assembler::expand(TokenList &list) { |
TokenList newlist; |
// Perform macro substitution |
for(auto &token: list) { |
auto it=_macros.find(token); |
// Note: we don't expand a macro identifier in the #define statement |
// since that would lead to counter-intuitive results |
if(it==_macros.end()|| |
(newlist.size()==1&&newlist[0]=="#define")|| |
(newlist.size()==3&&newlist[1]==":"&&newlist[2]=="#define")) |
newlist.push_back(std::move(token)); |
else for(auto const &replace: it->second) newlist.push_back(replace); |
} |
list=std::move(newlist); |
} |
|
void Assembler::elaborate(TokenList &list) { |
if(list.empty()) return; |
|
// Process label (if present) |
if(list.size()>=2&&list[1]==":") { |
if(!validateIdentifier(list[0])) |
throw std::runtime_error("Ill-formed identifier: \""+list[0]+"\""); |
_currentLabels.push_back(std::move(list[0])); |
list.erase(list.begin(),list.begin()+2); |
} |
|
if(list.empty()) return; |
|
// Process statement itself |
if(list[0][0]=='#') elaborateDirective(list); |
else { |
LinkableObject::Word rva; |
if(list[0][0]=='.') rva=elaborateDataDefinition(list); |
else rva=elaborateInstruction(list); |
|
for(auto const &label: _currentLabels) { |
_obj.addSymbol(label,rva); |
} |
_currentLabels.clear(); |
} |
} |
|
void Assembler::elaborateDirective(TokenList &list) { |
assert(!list.empty()); |
|
if(list[0]=="#define") { |
if(list.size()<3) |
throw std::runtime_error("Wrong number of tokens in the directive"); |
if(_macros.find(list[1])!=_macros.end()) |
throw std::runtime_error("Macro \""+list[1]+"\" has been already defined"); |
if(!validateIdentifier(list[1])) |
throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\""); |
_macros.emplace(list[1],TokenList(list.begin()+2,list.end())); |
} |
else if(list[0]=="#export") { |
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive"); |
if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\""); |
_exportedSymbols.push_back(list[1]); |
} |
else if(list[0]=="#import") { |
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive"); |
if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\""); |
_obj.addImportedSymbol(list[1]); |
} |
else if(list[0]=="#include") { |
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive"); |
auto filename=Utils::dequoteString(list[1]); |
if(Utils::isAbsolutePath(filename)) return processFileRecursive(filename); |
else { |
auto path=Utils::relativePath(currentFileName(),filename); |
if(Utils::fileExists(path)) return processFileRecursive(path); |
else { |
for(auto const &dir: _includeSearchDirs) { |
path=Utils::nativeSeparators(dir+filename); |
if(Utils::fileExists(path)) return processFileRecursive(path); |
} |
} |
} |
throw std::runtime_error("Cannot locate include file \""+filename+"\""); |
} |
else if(list[0]=="#message") { |
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive"); |
auto msg=Utils::dequoteString(list[1]); |
std::cout<<currentFileName()<<":"<<line()<<": "<<msg<<std::endl; |
} |
else throw std::runtime_error("Unrecognized directive: \""+list[0]+"\""); |
} |
|
LinkableObject::Word Assembler::elaborateDataDefinition(TokenList &list) { |
assert(!list.empty()); |
|
LinkableObject::Word rva=0; |
|
if(list[0]==".align") { |
if(list.size()>2) throw std::runtime_error("Unexpected token: \""+list[2]+"\""); |
std::size_t align=4; |
if(list.size()>1) align=static_cast<std::size_t>(numericLiteral(list[1])); |
if(!Utils::isPowerOf2(align)) throw std::runtime_error("Alignment must be a power of 2"); |
if(align<4) throw std::runtime_error("Alignment must be at least 4"); |
rva=_obj.addPadding(align); |
} |
else if(list[0]==".reserve") { |
if(list.size()<2) throw std::runtime_error("Unexpected end of statement"); |
else if(list.size()>2) throw std::runtime_error("Unexpected token: \""+list[2]+"\""); |
auto n=static_cast<std::size_t>(numericLiteral(list[1])); |
rva=_obj.addZeros(n); |
} |
else if(list[0]==".word") { |
if(list.size()<2) throw std::runtime_error("Unexpected end of statement"); |
for(std::size_t i=1;i<list.size();i++) { |
if(i%2!=0) { |
auto w=static_cast<LinkableObject::Word>(numericLiteral(list[i])); |
auto r=_obj.addWord(w); |
if(i==1) rva=r; |
} |
else { |
if(list[i]!=",") throw std::runtime_error("Comma expected"); |
if(i+1==list.size()) throw std::runtime_error("Unexpected end of statement"); |
} |
} |
} |
else if(list[0]==".byte") { |
if(list.size()<2) throw std::runtime_error("Unexpected end of statement"); |
for(std::size_t i=1;i<list.size();i++) { |
if(i%2!=0) { |
if(list[i].at(0)=='\"') { // string literal |
auto bytes=Utils::dequoteString(list[i]); |
auto r=_obj.addBytes(reinterpret_cast<const LinkableObject::Byte*> |
(bytes.c_str()),bytes.size()); |
if(i==1) rva=r; |
} |
else { |
auto n=numericLiteral(list[i]); |
|
if(n>255||n<-128) throw std::runtime_error("\""+list[i]+"\": out of range"); |
|
auto b=static_cast<LinkableObject::Byte>(n); |
auto r=_obj.addByte(b); |
if(i==1) rva=r; |
} |
} |
else { |
if(list[i]!=",") throw std::runtime_error("Comma expected"); |
if(i+1==list.size()) throw std::runtime_error("Unexpected end of statement"); |
} |
} |
} |
else throw std::runtime_error("Unrecognized statement: \""+list[0]+"\""); |
|
return rva; |
} |
|
LinkableObject::Word Assembler::elaborateInstruction(TokenList &list) { |
assert(!list.empty()); |
auto rva=_obj.addPadding(); |
if(list[0]=="add") encodeAdd(list); |
else if(list[0]=="and") encodeAnd(list); |
else if(list[0]=="call") encodeCall(list); |
else if(list[0].substr(0,4)=="cjmp") encodeCjmpxx(list); |
else if(list[0]=="divs") encodeDivs(list); |
else if(list[0]=="divu") encodeDivu(list); |
else if(list[0]=="hlt") encodeHlt(list); |
else if(list[0]=="jmp") encodeJmp(list); |
else if(list[0]=="iret") encodeIret(list); |
else if(list[0]=="lc") encodeLc(list); |
else if(list[0]=="lcs") encodeLcs(list); |
else if(list[0]=="lsb") encodeLsb(list); |
else if(list[0]=="lub") encodeLub(list); |
else if(list[0]=="lw") encodeLw(list); |
else if(list[0]=="mods") encodeMods(list); |
else if(list[0]=="modu") encodeModu(list); |
else if(list[0]=="mov") encodeMov(list); |
else if(list[0]=="mul") encodeMul(list); |
else if(list[0]=="neg") encodeNeg(list); |
else if(list[0]=="nop") encodeNop(list); |
else if(list[0]=="not") encodeNot(list); |
else if(list[0]=="or") encodeOr(list); |
else if(list[0]=="ret") encodeRet(list); |
else if(list[0]=="sb") encodeSb(list); |
else if(list[0]=="sl") encodeSl(list); |
else if(list[0]=="srs") encodeSrs(list); |
else if(list[0]=="sru") encodeSru(list); |
else if(list[0]=="sub") encodeSub(list); |
else if(list[0]=="sw") encodeSw(list); |
else if(list[0]=="xor") encodeXor(list); |
else throw std::runtime_error("Unrecognized instruction: \""+list[0]+"\""); |
return rva; |
} |
|
bool Assembler::validateIdentifier(const std::string &str) { |
/* |
* Valid identifier must satisfy the following requirements: |
* 1. Must not be empty |
* 2. The first character must be either alphabetic or an underscore |
* 3. Subsequent characters must be either alphanumeric or underscores |
*/ |
if(str.empty()) return false; |
for(std::size_t i=0;i<str.size();i++) { |
char ch=str[i]; |
if(i==0) { |
if(!std::isalpha(ch)&&ch!='_') return false; |
} |
else { |
if(!std::isalnum(ch)&&ch!='_') return false; |
} |
} |
return true; |
} |
|
Assembler::Integer Assembler::numericLiteral(const std::string &str) { |
std::size_t pos; |
Integer i; |
try { |
i=std::stoll(str,&pos,0); |
} |
catch(std::exception &) { |
throw std::runtime_error("Ill-formed numeric literal: \""+str+"\""); |
} |
if(pos<str.size()) throw std::runtime_error("Ill-formed numeric literal: \""+str+"\""); |
|
typedef std::make_signed<LinkableObject::Word>::type SignedWord; |
|
if(i>static_cast<Integer>(std::numeric_limits<LinkableObject::Word>::max())|| |
i<static_cast<Integer>(std::numeric_limits<SignedWord>::min())) |
throw std::runtime_error("\""+str+"\": out of range"); |
|
return i; |
} |
|
std::vector<Assembler::Operand> Assembler::getOperands(const TokenList &list) { |
std::vector<Operand> arglist; |
for(std::size_t i=1;i<list.size();i++) { |
if(i%2!=0) { |
Operand a; |
a.str=list[i]; |
|
if(!list[i].empty()&&list[i][0]=='r') { |
// Is argument a register? |
char *endptr; |
auto regstr=list[i].substr(1); |
auto reg=std::strtol(regstr.c_str(),&endptr,10); |
|
if(!*endptr&®>=0&®<=255) { |
a.type=Operand::Register; |
a.reg=static_cast<std::uint8_t>(reg); |
arglist.push_back(std::move(a)); |
continue; |
} |
} |
|
// Try alternative register names |
if(list[i]=="sp") { // stack pointer |
a.type=Operand::Register; |
a.reg=255; |
arglist.push_back(std::move(a)); |
} |
else if(list[i]=="rp") { // return pointer |
a.type=Operand::Register; |
a.reg=254; |
arglist.push_back(std::move(a)); |
} |
else if(list[i]=="irp") { // interrupt return pointer |
a.type=Operand::Register; |
a.reg=253; |
arglist.push_back(std::move(a)); |
} |
else if(list[i]=="cr") { // control register |
a.type=Operand::Register; |
a.reg=252; |
arglist.push_back(std::move(a)); |
} |
else if(list[i].size()==3&&list[i].substr(0,2)=="iv"&& |
list[i][2]>='0'&&list[i][2]<='7') // interrupt vector |
{ |
a.type=Operand::Register; |
a.reg=240+(list[i][2]-'0'); |
arglist.push_back(std::move(a)); |
} |
else if(validateIdentifier(list[i])) { |
// Is argument an identifier? |
a.type=Operand::Identifier; |
arglist.push_back(std::move(a)); |
} |
else { |
auto atpos=list[i].find_first_of('@'); |
if(atpos!=std::string::npos) { |
// Identifier with an offset? |
a.type=Operand::Identifier; |
a.str=list[i].substr(0,atpos); |
if(!validateIdentifier(a.str)) throw std::runtime_error("Ill-formed identifier"); |
a.i=numericLiteral(list[i].substr(atpos+1)); |
arglist.push_back(std::move(a)); |
} |
else { |
// Numeric literal? |
a.type=Operand::NumericLiteral; |
a.i=numericLiteral(list[i]); |
arglist.push_back(std::move(a)); |
} |
} |
} |
else { |
if(list[i]!=",") throw std::runtime_error("Comma expected"); |
if(i+1==list.size()) throw std::runtime_error("Unexpected end of line"); |
} |
} |
return arglist; |
} |
|
/* |
* Member functions to encode LXP32 instructions |
*/ |
|
void Assembler::encodeDstOperand(LinkableObject::Word &word,const Operand &arg) { |
if(arg.type!=Operand::Register) |
throw std::runtime_error("\""+arg.str+"\": must be a register"); |
word|=arg.reg<<16; |
} |
|
void Assembler::encodeRd1Operand(LinkableObject::Word &word,const Operand &arg) { |
if(arg.type==Operand::Register) { |
word|=0x02000000; |
word|=arg.reg<<8; |
} |
else if(arg.type==Operand::NumericLiteral) { |
if((arg.i<-128||arg.i>127)&&(arg.i<0xFFFFFF80||arg.i>0xFFFFFFFF)) |
throw std::runtime_error("\""+arg.str+"\": out of range"); |
auto b=static_cast<LinkableObject::Byte>(arg.i); |
word|=b<<8; |
} |
else throw std::runtime_error("\""+arg.str+"\": bad argument"); |
} |
|
void Assembler::encodeRd2Operand(LinkableObject::Word &word,const Operand &arg) { |
if(arg.type==Operand::Register) { |
word|=0x01000000; |
word|=arg.reg; |
} |
else if(arg.type==Operand::NumericLiteral) { |
if((arg.i<-128||arg.i>127)&&(arg.i<0xFFFFFF80||arg.i>0xFFFFFFFF)) |
throw std::runtime_error("\""+arg.str+"\": out of range"); |
auto b=static_cast<LinkableObject::Byte>(arg.i); |
word|=b; |
} |
else throw std::runtime_error("\""+arg.str+"\": bad argument"); |
} |
|
void Assembler::encodeAdd(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("add instruction requires 3 operands"); |
LinkableObject::Word w=0x40000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeAnd(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("and instruction requires 3 operands"); |
LinkableObject::Word w=0x60000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeCall(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=1) throw std::runtime_error("call instruction requires 1 operand"); |
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register"); |
LinkableObject::Word w=0x86FE0000; |
encodeRd1Operand(w,args[0]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeCjmpxx(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("cjmpxx instruction requires 3 operands"); |
|
LinkableObject::Word w; |
bool reverse=false; |
/* |
* Note: cjmpul, cjmpule, cjmpsl and cjmpsle don't have distinct opcodes; |
* instead, they are aliases for respective "g" or "ge" instructions |
* with reversed operand order. |
*/ |
if(list[0]=="cjmpe") w=0xE0000000; |
else if(list[0]=="cjmpne") w=0xD0000000; |
else if(list[0]=="cjmpug"||list[0]=="cjmpul") w=0xC8000000; |
else if(list[0]=="cjmpuge"||list[0]=="cjmpule") w=0xE8000000; |
else if(list[0]=="cjmpsg"||list[0]=="cjmpsl") w=0xC4000000; |
else if(list[0]=="cjmpsge"||list[0]=="cjmpsle") w=0xE4000000; |
else throw std::runtime_error("Unrecognized instruction: \""+list[0]+"\""); |
|
if(list[0]=="cjmpul"||list[0]=="cjmpule"|| |
list[0]=="cjmpsl"||list[0]=="cjmpsle") reverse=true; |
|
encodeDstOperand(w,args[0]); |
|
if(!reverse) { |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
} |
else { |
encodeRd1Operand(w,args[2]); |
encodeRd2Operand(w,args[1]); |
} |
_obj.addWord(w); |
} |
|
void Assembler::encodeDivs(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("divs instruction requires 3 operands"); |
LinkableObject::Word w=0x54000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeDivu(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("divu instruction requires 3 operands"); |
LinkableObject::Word w=0x50000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeHlt(const TokenList &list) { |
auto args=getOperands(list); |
if(!args.empty()) throw std::runtime_error("hlt instruction doesn't take operands"); |
_obj.addWord(0x08000000); |
} |
|
void Assembler::encodeJmp(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=1) throw std::runtime_error("jmp instruction requires 1 operand"); |
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register"); |
LinkableObject::Word w=0x82000000; |
encodeRd1Operand(w,args[0]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeIret(const TokenList &list) { |
// Note: "iret" is not a real instruction, but an alias for "jmp irp" |
auto args=getOperands(list); |
if(!args.empty()) throw std::runtime_error("iret instruction doesn't take operands"); |
_obj.addWord(0x8200FD00); |
} |
|
void Assembler::encodeLc(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lc instruction requires 2 operands"); |
|
LinkableObject::Word w=0x04000000; |
encodeDstOperand(w,args[0]); |
_obj.addWord(w); |
|
if(args[1].type==Operand::Identifier) { |
LinkableObject::Reference ref; |
ref.source=currentFileName(); |
ref.line=line(); |
ref.rva=_obj.addWord(0); |
ref.offset=args[1].i; |
ref.type=LinkableObject::Regular; |
_obj.addReference(args[1].str,ref); |
} |
else if(args[1].type==Operand::NumericLiteral) { |
_obj.addWord(static_cast<LinkableObject::Word>(args[1].i)); |
} |
else throw std::runtime_error("\""+args[1].str+"\": bad argument"); |
} |
|
void Assembler::encodeLcs(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lcs instruction requires 2 operands"); |
|
LinkableObject::Word w=0xA0000000; |
encodeDstOperand(w,args[0]); |
|
if(args[1].type==Operand::NumericLiteral) { |
if((args[1].i<-1048576||args[1].i>1048575)&&(args[1].i<0xFFF00000||args[1].i>0xFFFFFFFF)) |
throw std::runtime_error("\""+args[1].str+"\": out of range"); |
auto c=static_cast<LinkableObject::Word>(args[1].i)&0x1FFFFF; |
w|=(c&0xFFFF); |
w|=((c<<8)&0x1F000000); |
_obj.addWord(w); |
} |
else if(args[1].type==Operand::Identifier) { |
LinkableObject::Reference ref; |
ref.source=currentFileName(); |
ref.line=line(); |
ref.rva=_obj.addWord(w); |
ref.offset=args[1].i; |
ref.type=LinkableObject::Short; |
_obj.addReference(args[1].str,ref); |
} |
else throw std::runtime_error("\""+args[1].str+"\": bad argument"); |
} |
|
void Assembler::encodeLsb(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lsb instruction requires 2 operands"); |
if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register"); |
LinkableObject::Word w=0x2E000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeLub(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lub instruction requires 2 operands"); |
if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register"); |
LinkableObject::Word w=0x2A000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeLw(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lw instruction requires 2 operands"); |
if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register"); |
LinkableObject::Word w=0x22000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeMods(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("mods instruction requires 3 operands"); |
LinkableObject::Word w=0x5C000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeModu(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("modu instruction requires 3 operands"); |
LinkableObject::Word w=0x58000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeMov(const TokenList &list) { |
// Note: "mov" is not a real instruction, but an alias for "add dst, src, 0" |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("mov instruction requires 2 operands"); |
LinkableObject::Word w=0x40000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeMul(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("mul instruction requires 3 operands"); |
LinkableObject::Word w=0x48000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeNeg(const TokenList &list) { |
// Note: "neg" is not a real instruction, but an alias for "sub dst, 0, src" |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("neg instruction requires 2 operands"); |
LinkableObject::Word w=0x44000000; |
encodeDstOperand(w,args[0]); |
encodeRd2Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeNop(const TokenList &list) { |
auto args=getOperands(list); |
if(!args.empty()) throw std::runtime_error("nop instruction doesn't take operands"); |
_obj.addWord(0); |
} |
|
void Assembler::encodeNot(const TokenList &list) { |
// Note: "not" is not a real instruction, but an alias for "xor dst, src, -1" |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("not instruction requires 2 operands"); |
LinkableObject::Word w=0x680000FF; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeOr(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("or instruction requires 3 operands"); |
LinkableObject::Word w=0x64000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeRet(const TokenList &list) { |
// Note: "ret" is not a real instruction, but an alias for "jmp rp" |
auto args=getOperands(list); |
if(!args.empty()) throw std::runtime_error("ret instruction doesn't take operands"); |
_obj.addWord(0x8200FE00); |
} |
|
void Assembler::encodeSb(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("sb instruction requires 2 operands"); |
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register"); |
if(args[1].type==Operand::NumericLiteral) { |
// If numeric literal value is between 128 and 255 (inclusive), convert |
// it to a signed byte to avoid exception in encodeRd2Operand() |
if(args[1].i>=128&&args[1].i<=255) args[1].i-=256; |
} |
LinkableObject::Word w=0x3A000000; |
encodeRd1Operand(w,args[0]); |
encodeRd2Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSl(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("sl instruction requires 3 operands"); |
if(args[2].type==Operand::NumericLiteral&& |
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word)))) |
{ |
std::cerr<<currentFileName()<<":"<<line()<<": "; |
std::cerr<<"Warning: Bitwise shift result is undefined when " |
"the second operand is negative or greater than 31"<<std::endl; |
} |
|
LinkableObject::Word w=0x70000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSrs(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("srs instruction requires 3 operands"); |
if(args[2].type==Operand::NumericLiteral&& |
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word)))) |
{ |
std::cerr<<currentFileName()<<":"<<line()<<": "; |
std::cerr<<"Warning: Bitwise shift result is undefined when " |
"the second operand is negative or greater than 31"<<std::endl; |
} |
|
LinkableObject::Word w=0x7C000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSru(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("sru instruction requires 3 operands"); |
if(args[2].type==Operand::NumericLiteral&& |
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word)))) |
{ |
std::cerr<<currentFileName()<<":"<<line()<<": "; |
std::cerr<<"Warning: Bitwise shift result is undefined when " |
"the second operand is negative or greater than 31"<<std::endl; |
} |
|
LinkableObject::Word w=0x78000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSub(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("sub instruction requires 3 operands"); |
LinkableObject::Word w=0x44000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSw(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("sw instruction requires 2 operands"); |
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register"); |
LinkableObject::Word w=0x32000000; |
encodeRd1Operand(w,args[0]); |
encodeRd2Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeXor(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("xor instruction requires 3 operands"); |
LinkableObject::Word w=0x68000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
/lxp32asm/assembler.h
1,108 → 1,106
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Assembler class which performs |
* compilation of LXP32 assembly source files. |
*/ |
|
#ifndef ASSEMBLER_H_INCLUDED |
#define ASSEMBLER_H_INCLUDED |
|
#include "linkableobject.h" |
|
#include <vector> |
#include <map> |
#include <string> |
#include <cstdint> |
|
class Assembler { |
typedef std::vector<std::string> TokenList; |
typedef std::int_least64_t Integer; |
enum LexerState { |
Initial, |
Word, |
StringLiteral, |
BlockComment |
}; |
struct Operand { |
enum Type {Null,Register,Identifier,NumericLiteral}; |
Type type=Null; |
std::string str; |
Integer i=0; |
std::uint8_t reg=0; |
}; |
|
LinkableObject _obj; |
std::map<std::string,TokenList> _macros; |
LexerState _state; |
int _line; |
std::vector<std::string> _currentLabels; |
std::string _currentFileName; |
std::vector<std::string> _includeSearchDirs; |
std::vector<std::string> _exportedSymbols; |
std::vector<bool> _sectionEnabled; |
public: |
void processFile(const std::string &filename); |
|
void addIncludeSearchDir(const std::string &dir); |
|
int line() const; |
std::string currentFileName() const; |
|
LinkableObject &object(); |
const LinkableObject &object() const; |
private: |
void processFileRecursive(const std::string &filename); |
TokenList tokenize(const std::string &str); |
void expand(TokenList &list); |
void elaborate(TokenList &list); |
|
void elaborateDirective(TokenList &list); |
LinkableObject::Word elaborateDataDefinition(TokenList &list); |
LinkableObject::Word elaborateInstruction(TokenList &list); |
|
bool isSectionEnabled() const; |
static bool validateIdentifier(const std::string &str); |
static Integer numericLiteral(const std::string &str); |
static std::vector<Operand> getOperands(const TokenList &list); |
|
// LXP32 instructions |
void encodeDstOperand(LinkableObject::Word &word,const Operand &arg); |
void encodeRd1Operand(LinkableObject::Word &word,const Operand &arg); |
void encodeRd2Operand(LinkableObject::Word &word,const Operand &arg); |
|
void encodeAdd(const TokenList &list); |
void encodeAnd(const TokenList &list); |
void encodeCall(const TokenList &list); |
void encodeCjmpxx(const TokenList &list); |
void encodeDivs(const TokenList &list); |
void encodeDivu(const TokenList &list); |
void encodeHlt(const TokenList &list); |
void encodeJmp(const TokenList &list); |
void encodeIret(const TokenList &list); |
void encodeLc(const TokenList &list); |
void encodeLcs(const TokenList &list); |
void encodeLsb(const TokenList &list); |
void encodeLub(const TokenList &list); |
void encodeLw(const TokenList &list); |
void encodeMods(const TokenList &list); |
void encodeModu(const TokenList &list); |
void encodeMov(const TokenList &list); |
void encodeMul(const TokenList &list); |
void encodeNeg(const TokenList &list); |
void encodeNop(const TokenList &list); |
void encodeNot(const TokenList &list); |
void encodeOr(const TokenList &list); |
void encodeRet(const TokenList &list); |
void encodeSb(const TokenList &list); |
void encodeSl(const TokenList &list); |
void encodeSrs(const TokenList &list); |
void encodeSru(const TokenList &list); |
void encodeSub(const TokenList &list); |
void encodeSw(const TokenList &list); |
void encodeXor(const TokenList &list); |
}; |
|
#endif |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Assembler class which performs |
* compilation of LXP32 assembly source files. |
*/ |
|
#ifndef ASSEMBLER_H_INCLUDED |
#define ASSEMBLER_H_INCLUDED |
|
#include "linkableobject.h" |
|
#include <vector> |
#include <map> |
#include <string> |
#include <cstdint> |
|
class Assembler { |
typedef std::vector<std::string> TokenList; |
typedef std::int_least64_t Integer; |
enum LexerState { |
Initial, |
Word, |
StringLiteral, |
BlockComment |
}; |
struct Operand { |
enum Type {Null,Register,Identifier,NumericLiteral}; |
Type type=Null; |
std::string str; |
Integer i=0; |
std::uint8_t reg=0; |
}; |
|
LinkableObject _obj; |
std::map<std::string,TokenList> _macros; |
LexerState _state; |
int _line; |
std::vector<std::string> _currentLabels; |
std::string _currentFileName; |
std::vector<std::string> _includeSearchDirs; |
std::vector<std::string> _exportedSymbols; |
public: |
void processFile(const std::string &filename); |
|
void addIncludeSearchDir(const std::string &dir); |
|
int line() const; |
std::string currentFileName() const; |
|
LinkableObject &object(); |
const LinkableObject &object() const; |
private: |
void processFileRecursive(const std::string &filename); |
TokenList tokenize(const std::string &str); |
void expand(TokenList &list); |
void elaborate(TokenList &list); |
|
void elaborateDirective(TokenList &list); |
LinkableObject::Word elaborateDataDefinition(TokenList &list); |
LinkableObject::Word elaborateInstruction(TokenList &list); |
|
static bool validateIdentifier(const std::string &str); |
static Integer numericLiteral(const std::string &str); |
static std::vector<Operand> getOperands(const TokenList &list); |
|
// LXP32 instructions |
void encodeDstOperand(LinkableObject::Word &word,const Operand &arg); |
void encodeRd1Operand(LinkableObject::Word &word,const Operand &arg); |
void encodeRd2Operand(LinkableObject::Word &word,const Operand &arg); |
|
void encodeAdd(const TokenList &list); |
void encodeAnd(const TokenList &list); |
void encodeCall(const TokenList &list); |
void encodeCjmpxx(const TokenList &list); |
void encodeDivs(const TokenList &list); |
void encodeDivu(const TokenList &list); |
void encodeHlt(const TokenList &list); |
void encodeJmp(const TokenList &list); |
void encodeIret(const TokenList &list); |
void encodeLc(const TokenList &list); |
void encodeLcs(const TokenList &list); |
void encodeLsb(const TokenList &list); |
void encodeLub(const TokenList &list); |
void encodeLw(const TokenList &list); |
void encodeMods(const TokenList &list); |
void encodeModu(const TokenList &list); |
void encodeMov(const TokenList &list); |
void encodeMul(const TokenList &list); |
void encodeNeg(const TokenList &list); |
void encodeNop(const TokenList &list); |
void encodeNot(const TokenList &list); |
void encodeOr(const TokenList &list); |
void encodeRet(const TokenList &list); |
void encodeSb(const TokenList &list); |
void encodeSl(const TokenList &list); |
void encodeSrs(const TokenList &list); |
void encodeSru(const TokenList &list); |
void encodeSub(const TokenList &list); |
void encodeSw(const TokenList &list); |
void encodeXor(const TokenList &list); |
}; |
|
#endif |
/lxp32asm/linkableobject.cpp
1,290 → 1,290
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the LinkableObject class. |
*/ |
|
#include "linkableobject.h" |
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
#include <stdexcept> |
#include <utility> |
#include <cassert> |
#include <cstdlib> |
|
std::string LinkableObject::name() const { |
return _name; |
} |
|
void LinkableObject::setName(const std::string &str) { |
_name=str; |
} |
|
LinkableObject::Word LinkableObject::virtualAddress() const { |
return _virtualAddress; |
} |
|
void LinkableObject::setVirtualAddress(Word addr) { |
_virtualAddress=addr; |
} |
|
LinkableObject::Byte *LinkableObject::code() { |
return _code.data(); |
} |
|
const LinkableObject::Byte *LinkableObject::code() const { |
return _code.data(); |
} |
|
std::size_t LinkableObject::codeSize() const { |
return _code.size(); |
} |
|
LinkableObject::Word LinkableObject::addWord(Word w) { |
auto rva=addPadding(sizeof(Word)); |
// Note: this code doesn't depend on host machine's endianness |
_code.push_back(static_cast<Byte>(w)); |
_code.push_back(static_cast<Byte>(w>>8)); |
_code.push_back(static_cast<Byte>(w>>16)); |
_code.push_back(static_cast<Byte>(w>>24)); |
return rva; |
} |
|
LinkableObject::Word LinkableObject::addByte(Byte b) { |
auto rva=static_cast<LinkableObject::Word>(_code.size()); |
_code.push_back(b); |
return rva; |
} |
|
LinkableObject::Word LinkableObject::addBytes(const Byte *p,std::size_t n) { |
auto rva=static_cast<LinkableObject::Word>(_code.size()); |
_code.insert(_code.end(),p,p+n); |
return rva; |
} |
|
LinkableObject::Word LinkableObject::addZeros(std::size_t n) { |
auto rva=static_cast<LinkableObject::Word>(_code.size()); |
_code.resize(_code.size()+n); |
return rva; |
} |
|
LinkableObject::Word LinkableObject::addPadding(std::size_t size) { |
auto padding=(size-_code.size()%size)%size; |
if(padding>0) _code.resize(_code.size()+padding); |
return static_cast<LinkableObject::Word>(_code.size()); |
} |
|
LinkableObject::Word LinkableObject::getWord(Word rva) const { |
Word w=0; |
if(rva<codeSize()) w|=static_cast<Word>(_code[rva++]); |
if(rva<codeSize()) w|=static_cast<Word>(_code[rva++])<<8; |
if(rva<codeSize()) w|=static_cast<Word>(_code[rva++])<<16; |
if(rva<codeSize()) w|=static_cast<Word>(_code[rva++])<<24; |
return w; |
} |
|
void LinkableObject::replaceWord(Word rva,Word value) { |
assert(rva+sizeof(Word)<=codeSize()); |
// Note: this code doesn't depend on host machine's endianness |
_code[rva++]=static_cast<Byte>(value); |
_code[rva++]=static_cast<Byte>(value>>8); |
_code[rva++]=static_cast<Byte>(value>>16); |
_code[rva++]=static_cast<Byte>(value>>24); |
} |
|
void LinkableObject::addSymbol(const std::string &name,Word rva) { |
auto &data=symbol(name); |
if(data.type!=Unknown) throw std::runtime_error("Symbol \""+name+"\" is already defined"); |
data.type=Local; |
data.rva=rva; |
} |
|
void LinkableObject::addImportedSymbol(const std::string &name) { |
auto &data=symbol(name); |
if(data.type!=Unknown) throw std::runtime_error("Symbol \""+name+"\" is already defined"); |
data.type=Imported; |
} |
|
void LinkableObject::exportSymbol(const std::string &name) { |
auto it=_symbols.find(name); |
if(it==_symbols.end()||it->second.type==Unknown) throw std::runtime_error("Undefined symbol \""+name+"\""); |
if(it->second.type==Imported) throw std::runtime_error("Symbol \""+name+"\" can't be both imported and exported at the same time"); |
if(it->second.type==Exported) throw std::runtime_error("Symbol \""+name+"\" has been already exported"); |
it->second.type=Exported; |
} |
|
void LinkableObject::addReference(const std::string &symbolName,const Reference &ref) { |
auto &data=symbol(symbolName); |
data.refs.push_back(ref); |
} |
|
LinkableObject::SymbolData &LinkableObject::symbol(const std::string &name) { |
return _symbols[name]; |
} |
|
const LinkableObject::SymbolData &LinkableObject::symbol(const std::string &name) const { |
auto const it=_symbols.find(name); |
if(it==_symbols.end()) throw std::runtime_error("Undefined symbol \""+name+"\""); |
return it->second; |
} |
|
const LinkableObject::SymbolTable &LinkableObject::symbols() const { |
return _symbols; |
} |
|
void LinkableObject::serialize(const std::string &filename) const { |
std::ofstream out(filename,std::ios_base::out); |
if(!out) throw std::runtime_error("Cannot open \""+filename+"\" for writing"); |
|
out<<"LinkableObject"<<std::endl; |
if(!_name.empty()) out<<"Name "<<Utils::urlEncode(_name)<<std::endl; |
out<<"VirtualAddress 0x"<<Utils::hex(_virtualAddress)<<std::endl; |
|
out<<std::endl; |
out<<"Start Code"<<std::endl; |
|
for(Word rva=0;rva<_code.size();rva+=sizeof(Word)) { |
out<<"\t0x"<<Utils::hex(getWord(rva))<<std::endl; |
} |
|
out<<"End Code"<<std::endl; |
|
for(auto const &sym: _symbols) { |
if(sym.second.type==Unknown) |
throw std::runtime_error("Undefined symbol: \""+sym.first+"\""); |
out<<std::endl; |
out<<"Start Symbol"<<std::endl; |
out<<"\tName "<<Utils::urlEncode(sym.first)<<std::endl; |
if(sym.second.type==Local) out<<"\tType Local"<<std::endl; |
else if(sym.second.type==Exported) out<<"\tType Exported"<<std::endl; |
else out<<"\tType Imported"<<std::endl; |
if(sym.second.type!=Imported) out<<"\tRVA 0x"<<Utils::hex(sym.second.rva)<<std::endl; |
for(auto const &ref: sym.second.refs) { |
out<<"\tRef "; |
out<<Utils::urlEncode(ref.source)<<" "; |
out<<ref.line<<" "; |
out<<"0x"<<Utils::hex(ref.rva)<<" "; |
out<<ref.offset<<" "; |
if(ref.type==Regular) out<<"Regular"<<std::endl; |
else if(ref.type==Short) out<<"Short"<<std::endl; |
} |
out<<"End Symbol"<<std::endl; |
} |
} |
|
void LinkableObject::deserialize(const std::string &filename) { |
std::ifstream in(filename,std::ios_base::in); |
if(!in) throw std::runtime_error("Cannot open \""+filename+"\""); |
|
operator=(LinkableObject()); |
|
std::string line; |
for(;;) { |
if(!std::getline(in,line)) throw std::runtime_error("Bad object format"); |
auto tokens=tokenize(line); |
if(tokens.empty()) continue; |
else if(tokens[0]!="LinkableObject") throw std::runtime_error("Bad object format"); |
break; |
} |
|
while(std::getline(in,line)) { |
auto tokens=tokenize(line); |
if(tokens.empty()) continue; |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
else if(tokens[0]=="Name") _name=Utils::urlDecode(tokens[1]); |
else if(tokens[0]=="VirtualAddress") _virtualAddress=std::strtoul(tokens[1].c_str(),NULL,0); |
else if(tokens[0]=="Start") { |
if(tokens[1]=="Code") deserializeCode(in); |
else if(tokens[1]=="Symbol") deserializeSymbol(in); |
else throw std::runtime_error("Unexpected token: \""+tokens[1]+"\""); |
} |
else throw std::runtime_error("Unexpected token: \""+tokens[0]+"\""); |
} |
} |
|
/* |
* Private members |
*/ |
|
void LinkableObject::deserializeCode(std::istream &in) { |
std::string line; |
while(std::getline(in,line)) { |
auto tokens=tokenize(line); |
if(tokens.empty()) continue; |
if(tokens[0]=="End") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
if(tokens[1]=="Code") return; |
throw std::runtime_error("Unexpected token: \""+tokens[1]+"\""); |
} |
auto w=static_cast<Word>(std::strtoul(tokens[0].c_str(),NULL,0)); |
addWord(w); |
} |
throw std::runtime_error("Unexpected end of file"); |
} |
|
void LinkableObject::deserializeSymbol(std::istream &in) { |
std::string line; |
std::string name; |
SymbolData data; |
while(std::getline(in,line)) { |
auto tokens=tokenize(line); |
if(tokens.empty()) continue; |
if(tokens[0]=="End") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
if(tokens[1]=="Symbol") { |
if(name.empty()) throw std::runtime_error("Symbol name is not defined"); |
if(data.type==Unknown) throw std::runtime_error("Bad symbol type"); |
_symbols.emplace(std::move(name),std::move(data)); |
return; |
} |
throw std::runtime_error("Unexpected token: \""+tokens[1]+"\""); |
} |
else if(tokens[0]=="Name") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
name=Utils::urlDecode(tokens[1]); |
} |
else if(tokens[0]=="Type") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
if(tokens[1]=="Local") data.type=Local; |
else if(tokens[1]=="Exported") data.type=Exported; |
else if(tokens[1]=="Imported") data.type=Imported; |
else throw std::runtime_error("Bad symbol type"); |
} |
else if(tokens[0]=="RVA") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
data.rva=std::strtoul(tokens[1].c_str(),NULL,0); |
} |
else if(tokens[0]=="Ref") { |
Reference ref; |
if(tokens.size()<4) throw std::runtime_error("Unexpected end of line"); |
ref.source=Utils::urlDecode(tokens[1]); |
ref.line=std::strtoul(tokens[2].c_str(),NULL,0); |
ref.rva=std::strtoul(tokens[3].c_str(),NULL,0); |
ref.offset=std::strtoll(tokens[4].c_str(),NULL,0); |
if(tokens[5]=="Regular") ref.type=Regular; |
else if(tokens[5]=="Short") ref.type=Short; |
else throw std::runtime_error("Invalid reference type: \""+tokens[5]+"\""); |
data.refs.push_back(std::move(ref)); |
} |
} |
throw std::runtime_error("Unexpected end of file"); |
} |
|
std::vector<std::string> LinkableObject::tokenize(const std::string &str) { |
std::vector<std::string> tokens; |
for(std::size_t pos=0;;) { |
auto start=str.find_first_not_of(" \t\r\n",pos); |
if(start==std::string::npos) return tokens; |
auto end=str.find_first_of(" \t\r\n",start); |
if(end==std::string::npos) { |
tokens.push_back(str.substr(start)); |
return tokens; |
} |
else tokens.push_back(str.substr(start,end-start)); |
pos=end; |
} |
} |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the LinkableObject class. |
*/ |
|
#include "linkableobject.h" |
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
#include <stdexcept> |
#include <utility> |
#include <cassert> |
#include <cstdlib> |
|
std::string LinkableObject::name() const { |
return _name; |
} |
|
void LinkableObject::setName(const std::string &str) { |
_name=str; |
} |
|
LinkableObject::Word LinkableObject::virtualAddress() const { |
return _virtualAddress; |
} |
|
void LinkableObject::setVirtualAddress(Word addr) { |
_virtualAddress=addr; |
} |
|
LinkableObject::Byte *LinkableObject::code() { |
return _code.data(); |
} |
|
const LinkableObject::Byte *LinkableObject::code() const { |
return _code.data(); |
} |
|
std::size_t LinkableObject::codeSize() const { |
return _code.size(); |
} |
|
LinkableObject::Word LinkableObject::addWord(Word w) { |
auto rva=addPadding(sizeof(Word)); |
// Note: this code doesn't depend on host machine's endianness |
_code.push_back(static_cast<Byte>(w)); |
_code.push_back(static_cast<Byte>(w>>8)); |
_code.push_back(static_cast<Byte>(w>>16)); |
_code.push_back(static_cast<Byte>(w>>24)); |
return rva; |
} |
|
LinkableObject::Word LinkableObject::addByte(Byte b) { |
auto rva=static_cast<LinkableObject::Word>(_code.size()); |
_code.push_back(b); |
return rva; |
} |
|
LinkableObject::Word LinkableObject::addBytes(const Byte *p,std::size_t n) { |
auto rva=static_cast<LinkableObject::Word>(_code.size()); |
_code.insert(_code.end(),p,p+n); |
return rva; |
} |
|
LinkableObject::Word LinkableObject::addZeros(std::size_t n) { |
auto rva=static_cast<LinkableObject::Word>(_code.size()); |
_code.resize(_code.size()+n); |
return rva; |
} |
|
LinkableObject::Word LinkableObject::addPadding(std::size_t size) { |
auto padding=(size-_code.size()%size)%size; |
if(padding>0) _code.resize(_code.size()+padding); |
return static_cast<LinkableObject::Word>(_code.size()); |
} |
|
LinkableObject::Word LinkableObject::getWord(Word rva) const { |
Word w=0; |
if(rva<codeSize()) w|=static_cast<Word>(_code[rva++]); |
if(rva<codeSize()) w|=static_cast<Word>(_code[rva++])<<8; |
if(rva<codeSize()) w|=static_cast<Word>(_code[rva++])<<16; |
if(rva<codeSize()) w|=static_cast<Word>(_code[rva++])<<24; |
return w; |
} |
|
void LinkableObject::replaceWord(Word rva,Word value) { |
assert(rva+sizeof(Word)<=codeSize()); |
// Note: this code doesn't depend on host machine's endianness |
_code[rva++]=static_cast<Byte>(value); |
_code[rva++]=static_cast<Byte>(value>>8); |
_code[rva++]=static_cast<Byte>(value>>16); |
_code[rva++]=static_cast<Byte>(value>>24); |
} |
|
void LinkableObject::addSymbol(const std::string &name,Word rva) { |
auto &data=symbol(name); |
if(data.type!=Unknown) throw std::runtime_error("Symbol \""+name+"\" is already defined"); |
data.type=Local; |
data.rva=rva; |
} |
|
void LinkableObject::addImportedSymbol(const std::string &name) { |
auto &data=symbol(name); |
if(data.type!=Unknown) throw std::runtime_error("Symbol \""+name+"\" is already defined"); |
data.type=Imported; |
} |
|
void LinkableObject::exportSymbol(const std::string &name) { |
auto it=_symbols.find(name); |
if(it==_symbols.end()||it->second.type==Unknown) throw std::runtime_error("Undefined symbol \""+name+"\""); |
if(it->second.type==Imported) throw std::runtime_error("Symbol \""+name+"\" can't be both imported and exported at the same time"); |
if(it->second.type==Exported) throw std::runtime_error("Symbol \""+name+"\" has been already exported"); |
it->second.type=Exported; |
} |
|
void LinkableObject::addReference(const std::string &symbolName,const Reference &ref) { |
auto &data=symbol(symbolName); |
data.refs.push_back(ref); |
} |
|
LinkableObject::SymbolData &LinkableObject::symbol(const std::string &name) { |
return _symbols[name]; |
} |
|
const LinkableObject::SymbolData &LinkableObject::symbol(const std::string &name) const { |
auto const it=_symbols.find(name); |
if(it==_symbols.end()) throw std::runtime_error("Undefined symbol \""+name+"\""); |
return it->second; |
} |
|
const LinkableObject::SymbolTable &LinkableObject::symbols() const { |
return _symbols; |
} |
|
void LinkableObject::serialize(const std::string &filename) const { |
std::ofstream out(filename,std::ios_base::out); |
if(!out) throw std::runtime_error("Cannot open \""+filename+"\" for writing"); |
|
out<<"LinkableObject"<<std::endl; |
if(!_name.empty()) out<<"Name "<<Utils::urlEncode(_name)<<std::endl; |
out<<"VirtualAddress 0x"<<Utils::hex(_virtualAddress)<<std::endl; |
|
out<<std::endl; |
out<<"Start Code"<<std::endl; |
|
for(Word rva=0;rva<_code.size();rva+=sizeof(Word)) { |
out<<"\t0x"<<Utils::hex(getWord(rva))<<std::endl; |
} |
|
out<<"End Code"<<std::endl; |
|
for(auto const &sym: _symbols) { |
if(sym.second.type==Unknown) |
throw std::runtime_error("Undefined symbol: \""+sym.first+"\""); |
out<<std::endl; |
out<<"Start Symbol"<<std::endl; |
out<<"\tName "<<Utils::urlEncode(sym.first)<<std::endl; |
if(sym.second.type==Local) out<<"\tType Local"<<std::endl; |
else if(sym.second.type==Exported) out<<"\tType Exported"<<std::endl; |
else out<<"\tType Imported"<<std::endl; |
if(sym.second.type!=Imported) out<<"\tRVA 0x"<<Utils::hex(sym.second.rva)<<std::endl; |
for(auto const &ref: sym.second.refs) { |
out<<"\tRef "; |
out<<Utils::urlEncode(ref.source)<<" "; |
out<<ref.line<<" "; |
out<<"0x"<<Utils::hex(ref.rva)<<" "; |
out<<ref.offset<<" "; |
if(ref.type==Regular) out<<"Regular"<<std::endl; |
else if(ref.type==Short) out<<"Short"<<std::endl; |
} |
out<<"End Symbol"<<std::endl; |
} |
} |
|
void LinkableObject::deserialize(const std::string &filename) { |
std::ifstream in(filename,std::ios_base::in); |
if(!in) throw std::runtime_error("Cannot open \""+filename+"\""); |
|
operator=(LinkableObject()); |
|
std::string line; |
for(;;) { |
if(!std::getline(in,line)) throw std::runtime_error("Bad object format"); |
auto tokens=tokenize(line); |
if(tokens.empty()) continue; |
else if(tokens[0]!="LinkableObject") throw std::runtime_error("Bad object format"); |
break; |
} |
|
while(std::getline(in,line)) { |
auto tokens=tokenize(line); |
if(tokens.empty()) continue; |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
else if(tokens[0]=="Name") _name=Utils::urlDecode(tokens[1]); |
else if(tokens[0]=="VirtualAddress") _virtualAddress=std::strtoul(tokens[1].c_str(),NULL,0); |
else if(tokens[0]=="Start") { |
if(tokens[1]=="Code") deserializeCode(in); |
else if(tokens[1]=="Symbol") deserializeSymbol(in); |
else throw std::runtime_error("Unexpected token: \""+tokens[1]+"\""); |
} |
else throw std::runtime_error("Unexpected token: \""+tokens[0]+"\""); |
} |
} |
|
/* |
* Private members |
*/ |
|
void LinkableObject::deserializeCode(std::istream &in) { |
std::string line; |
while(std::getline(in,line)) { |
auto tokens=tokenize(line); |
if(tokens.empty()) continue; |
if(tokens[0]=="End") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
if(tokens[1]=="Code") return; |
throw std::runtime_error("Unexpected token: \""+tokens[1]+"\""); |
} |
auto w=static_cast<Word>(std::strtoul(tokens[0].c_str(),NULL,0)); |
addWord(w); |
} |
throw std::runtime_error("Unexpected end of file"); |
} |
|
void LinkableObject::deserializeSymbol(std::istream &in) { |
std::string line; |
std::string name; |
SymbolData data; |
while(std::getline(in,line)) { |
auto tokens=tokenize(line); |
if(tokens.empty()) continue; |
if(tokens[0]=="End") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
if(tokens[1]=="Symbol") { |
if(name.empty()) throw std::runtime_error("Symbol name is not defined"); |
if(data.type==Unknown) throw std::runtime_error("Bad symbol type"); |
_symbols.emplace(std::move(name),std::move(data)); |
return; |
} |
throw std::runtime_error("Unexpected token: \""+tokens[1]+"\""); |
} |
else if(tokens[0]=="Name") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
name=Utils::urlDecode(tokens[1]); |
} |
else if(tokens[0]=="Type") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
if(tokens[1]=="Local") data.type=Local; |
else if(tokens[1]=="Exported") data.type=Exported; |
else if(tokens[1]=="Imported") data.type=Imported; |
else throw std::runtime_error("Bad symbol type"); |
} |
else if(tokens[0]=="RVA") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
data.rva=std::strtoul(tokens[1].c_str(),NULL,0); |
} |
else if(tokens[0]=="Ref") { |
Reference ref; |
if(tokens.size()<4) throw std::runtime_error("Unexpected end of line"); |
ref.source=Utils::urlDecode(tokens[1]); |
ref.line=std::strtoul(tokens[2].c_str(),NULL,0); |
ref.rva=std::strtoul(tokens[3].c_str(),NULL,0); |
ref.offset=std::strtoll(tokens[4].c_str(),NULL,0); |
if(tokens[5]=="Regular") ref.type=Regular; |
else if(tokens[5]=="Short") ref.type=Short; |
else throw std::runtime_error("Invalid reference type: \""+tokens[5]+"\""); |
data.refs.push_back(std::move(ref)); |
} |
} |
throw std::runtime_error("Unexpected end of file"); |
} |
|
std::vector<std::string> LinkableObject::tokenize(const std::string &str) { |
std::vector<std::string> tokens; |
for(std::size_t pos=0;;) { |
auto start=str.find_first_not_of(" \t\r\n",pos); |
if(start==std::string::npos) return tokens; |
auto end=str.find_first_of(" \t\r\n",start); |
if(end==std::string::npos) { |
tokens.push_back(str.substr(start)); |
return tokens; |
} |
else tokens.push_back(str.substr(start,end-start)); |
pos=end; |
} |
} |
/lxp32asm/linkableobject.h
1,88 → 1,88
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the LinkableObject class which represents |
* compiled LXP32 binary code. |
*/ |
|
#ifndef LINKABLEOBJECT_H_INCLUDED |
#define LINKABLEOBJECT_H_INCLUDED |
|
#include <iostream> |
#include <vector> |
#include <map> |
#include <string> |
#include <cstdint> |
|
class LinkableObject { |
public: |
typedef unsigned char Byte; |
typedef std::uint32_t Word; |
typedef std::int_least64_t Integer; |
|
enum SymbolType {Unknown,Local,Exported,Imported}; |
enum RefType {Regular,Short}; |
|
struct Reference { |
std::string source; |
int line; |
Word rva; |
Integer offset; |
RefType type; |
}; |
struct SymbolData { |
SymbolType type=Unknown; |
Word rva; |
std::vector<Reference> refs; |
}; |
|
typedef std::map<std::string,SymbolData> SymbolTable; |
|
private: |
std::string _name; |
std::vector<Byte> _code; |
SymbolTable _symbols; |
Word _virtualAddress=0; |
|
public: |
std::string name() const; |
void setName(const std::string &str); |
|
Word virtualAddress() const; |
void setVirtualAddress(Word addr); |
|
Byte *code(); |
const Byte *code() const; |
std::size_t codeSize() const; |
|
Word addWord(Word w); |
Word addByte(Byte b); |
Word addBytes(const Byte *p,std::size_t n); |
Word addZeros(std::size_t n); |
|
Word addPadding(std::size_t size=sizeof(LinkableObject::Word)); |
|
Word getWord(Word rva) const; |
void replaceWord(Word rva,Word value); |
|
void addSymbol(const std::string &name,Word rva); |
void addImportedSymbol(const std::string &name); |
void exportSymbol(const std::string &name); |
void addReference(const std::string &symbolName,const Reference &ref); |
|
SymbolData &symbol(const std::string &name); |
const SymbolData &symbol(const std::string &name) const; |
const SymbolTable &symbols() const; |
|
void serialize(const std::string &filename) const; |
void deserialize(const std::string &filename); |
|
private: |
void deserializeCode(std::istream &in); |
void deserializeSymbol(std::istream &in); |
static std::vector<std::string> tokenize(const std::string &str); |
}; |
|
#endif |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the LinkableObject class which represents |
* compiled LXP32 binary code. |
*/ |
|
#ifndef LINKABLEOBJECT_H_INCLUDED |
#define LINKABLEOBJECT_H_INCLUDED |
|
#include <iostream> |
#include <vector> |
#include <map> |
#include <string> |
#include <cstdint> |
|
class LinkableObject { |
public: |
typedef unsigned char Byte; |
typedef std::uint32_t Word; |
typedef std::int_least64_t Integer; |
|
enum SymbolType {Unknown,Local,Exported,Imported}; |
enum RefType {Regular,Short}; |
|
struct Reference { |
std::string source; |
int line; |
Word rva; |
Integer offset; |
RefType type; |
}; |
struct SymbolData { |
SymbolType type=Unknown; |
Word rva; |
std::vector<Reference> refs; |
}; |
|
typedef std::map<std::string,SymbolData> SymbolTable; |
|
private: |
std::string _name; |
std::vector<Byte> _code; |
SymbolTable _symbols; |
Word _virtualAddress=0; |
|
public: |
std::string name() const; |
void setName(const std::string &str); |
|
Word virtualAddress() const; |
void setVirtualAddress(Word addr); |
|
Byte *code(); |
const Byte *code() const; |
std::size_t codeSize() const; |
|
Word addWord(Word w); |
Word addByte(Byte b); |
Word addBytes(const Byte *p,std::size_t n); |
Word addZeros(std::size_t n); |
|
Word addPadding(std::size_t size=sizeof(LinkableObject::Word)); |
|
Word getWord(Word rva) const; |
void replaceWord(Word rva,Word value); |
|
void addSymbol(const std::string &name,Word rva); |
void addImportedSymbol(const std::string &name); |
void exportSymbol(const std::string &name); |
void addReference(const std::string &symbolName,const Reference &ref); |
|
SymbolData &symbol(const std::string &name); |
const SymbolData &symbol(const std::string &name) const; |
const SymbolTable &symbols() const; |
|
void serialize(const std::string &filename) const; |
void deserialize(const std::string &filename); |
|
private: |
void deserializeCode(std::istream &in); |
void deserializeSymbol(std::istream &in); |
static std::vector<std::string> tokenize(const std::string &str); |
}; |
|
#endif |
/lxp32asm/linker.cpp
1,272 → 1,272
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Linker class. |
*/ |
|
#include "linker.h" |
|
#include "linkableobject.h" |
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
#include <sstream> |
#include <map> |
#include <stdexcept> |
#include <cassert> |
#include <algorithm> |
|
void Linker::addObject(LinkableObject &obj) { |
_objects.push_back(&obj); |
} |
|
void Linker::link(OutputWriter &writer) { |
if(_objects.empty()) throw std::runtime_error("Object set is empty"); |
|
// Merge symbol tables |
buildSymbolTable(); |
|
// Determine entry point |
if(_objects.size()==1) _entryObject=_objects[0]; |
else if(_entryObject==nullptr) |
throw std::runtime_error("Entry point not defined: cannot find \"entry\" or \"Entry\" symbol"); |
|
// Assign virtual addresses |
placeObjects(); |
|
// Perform relocations |
for(auto &obj: _objects) relocateObject(obj); |
|
// Write binary data |
writeObjects(writer); |
_bytesWritten=writer.size(); |
} |
|
void Linker::setBase(LinkableObject::Word base) { |
_base=base; |
} |
|
void Linker::setAlignment(std::size_t align) { |
_align=align; |
} |
|
void Linker::setImageSize(std::size_t size) { |
_imageSize=size; |
} |
|
void Linker::generateMap(std::ostream &s) { |
// Calculate the maximum length of a symbol name |
std::size_t len=0; |
for(auto const &obj: _objects) { |
for(auto const &sym: obj->symbols()) { |
if(sym.second.type!=LinkableObject::Imported) |
len=std::max(len,sym.first.size()); |
} |
} |
len=std::max(len+3,std::size_t(8)); // width of the first column |
|
s<<"Image base address: "<<Utils::hex(_base)<<std::endl; |
s<<"Object alignment: "<<_align<<std::endl; |
s<<"Image size: "<<(_bytesWritten/4)<<" words"<<std::endl; |
s<<"Number of objects: "<<_objects.size()<<std::endl; |
s<<std::endl; |
|
for(auto const &obj: _objects) { |
s<<"Object \""<<obj->name()<<"\" at address "<<Utils::hex(obj->virtualAddress())<<std::endl; |
s<<std::endl; |
std::multimap<LinkableObject::Word,std::pair<std::string,LinkableObject::SymbolData> > sorted; |
for(auto const &sym: obj->symbols()) sorted.emplace(sym.second.rva,sym); |
for(auto const &sym: sorted) { |
if(sym.second.second.type==LinkableObject::Imported) continue; |
s<<sym.second.first; |
s<<std::string(len-sym.second.first.size(),' '); |
s<<Utils::hex(obj->virtualAddress()+sym.second.second.rva); |
if(sym.second.second.type==LinkableObject::Local) s<<" Local"; |
else s<<" Exported"; |
s<<std::endl; |
} |
s<<std::endl; |
} |
} |
|
/* |
* Private members |
*/ |
|
void Linker::buildSymbolTable() { |
_globalSymbolTable.clear(); |
|
// Build a table of exported symbols from all modules |
for(auto const &obj: _objects) { |
auto const &table=obj->symbols(); |
for(auto const &item: table) { |
if((item.first=="entry"||item.first=="Entry")&&item.second.type!=LinkableObject::Imported) { |
if(_entryObject) { |
std::ostringstream msg; |
msg<<obj->name()<<": Duplicate definition of the entry symbol "; |
msg<<"(previously defined in "<<_entryObject->name()<<")"; |
throw std::runtime_error(msg.str()); |
} |
if(item.second.rva!=0) { |
std::ostringstream msg; |
msg<<obj->name()<<": "; |
msg<<"Entry point must refer to the start of the object"; |
throw std::runtime_error(msg.str()); |
} |
_entryObject=obj; |
} |
if(item.second.type==LinkableObject::Local) continue; |
// Insert item to the global symbol table if it doesn't exist yet |
auto it=_globalSymbolTable.emplace(item.first,GlobalSymbolData()).first; |
|
// Check that the symbol has not been already defined in another object |
if(item.second.type==LinkableObject::Exported) { |
if(it->second.obj) { |
std::ostringstream msg; |
msg<<obj->name()<<": Duplicate definition of \""<<item.first; |
msg<<"\" (previously defined in "<<it->second.obj->name()<<")"; |
throw std::runtime_error(msg.str()); |
} |
it->second.obj=obj; |
it->second.rva=item.second.rva; |
} |
|
if(!item.second.refs.empty()) it->second.refs.insert(obj); |
} |
} |
|
// Check that local symbols don't shadow the public ones |
for(auto const &obj: _objects) { |
auto const &table=obj->symbols(); |
for(auto const &item: table) { |
if(item.second.type!=LinkableObject::Local) continue; |
auto it=_globalSymbolTable.find(item.first); |
if(it==_globalSymbolTable.end()) continue; |
if(!it->second.obj) continue; |
if(item.first==it->first) { |
std::ostringstream msg; |
msg<<obj->name()<<": Local symbol \""<<item.first<<"\" shadows the public one "; |
msg<<"(defined in "<<it->second.obj->name()<<")"; |
throw std::runtime_error(msg.str()); |
} |
} |
} |
|
// Check that no undefined symbols remain |
for(auto const &item: _globalSymbolTable) { |
if(item.second.obj==nullptr&&!item.second.refs.empty()) { |
std::ostringstream msg; |
msg<<"Undefined symbol: \""<<item.first<<"\""; |
auto const it=item.second.refs.begin(); |
msg<<" (referenced from "<<(*it)->name()<<")"; |
throw std::runtime_error(msg.str()); |
} |
} |
} |
|
void Linker::placeObjects() { |
auto currentBase=_base; |
|
// Make entry object the first |
if(_objects.size()>1) { |
for(auto it=_objects.begin();it!=_objects.end();++it) { |
if(*it==_entryObject) { |
_objects.erase(it); |
break; |
} |
} |
_objects.insert(_objects.begin(),_entryObject); |
} |
|
// Remove unreferenced objects |
if(_objects.size()>1) { |
std::set<const LinkableObject*> used; |
markAsUsed(_objects[0],used); |
for(auto it=_objects.begin();it!=_objects.end();) { |
if(used.find(*it)==used.end()) { |
std::cerr<<"Linker warning: skipping an unreferenced object \""; |
std::cerr<<(*it)->name()<<"\""<<std::endl; |
for(auto sym=_globalSymbolTable.begin();sym!=_globalSymbolTable.end();) { |
if(sym->second.obj==*it) sym=_globalSymbolTable.erase(sym); |
else ++sym; |
} |
it=_objects.erase(it); |
} |
else ++it; |
} |
} |
|
// Set base addresses |
for(auto it=_objects.begin();it!=_objects.end();++it) { |
(*it)->setVirtualAddress(currentBase); |
if(it+1!=_objects.end()) (*it)->addPadding(_align); |
else (*it)->addPadding(); |
currentBase+=static_cast<LinkableObject::Word>((*it)->codeSize()); |
} |
} |
|
void Linker::relocateObject(LinkableObject *obj) { |
for(auto const &sym: obj->symbols()) { |
LinkableObject::Word addr; |
if(sym.second.refs.empty()) continue; |
|
if(sym.second.type==LinkableObject::Local) addr=obj->virtualAddress()+sym.second.rva; |
else { |
auto it=_globalSymbolTable.find(sym.first); |
assert(it!=_globalSymbolTable.end()); |
assert(it->second.obj); |
addr=it->second.obj->virtualAddress()+it->second.rva; |
} |
|
for(auto const &ref: sym.second.refs) { |
if(ref.type==LinkableObject::Regular) obj->replaceWord(ref.rva,addr+ref.offset); |
else { |
auto target=static_cast<LinkableObject::Word>(addr+ref.offset); |
if(target>0xFFFFF&&target<0xFFF00000) { |
std::ostringstream msg; |
msg<<"Address 0x"<<Utils::hex(target)<<" is out of the range for a short reference"; |
msg<<" (referenced from "<<ref.source<<":"<<ref.line<<")"; |
throw std::runtime_error(msg.str()); |
} |
target&=0x1FFFFF; |
auto w=obj->getWord(ref.rva); |
w|=(target&0xFFFF); |
w|=((target<<8)&0x1F000000); |
obj->replaceWord(ref.rva,w); |
} |
} |
} |
} |
|
void Linker::writeObjects(OutputWriter &writer) { |
std::size_t currentSize=0; |
// Write entry object |
writer.write(reinterpret_cast<const char*>(_entryObject->code()),_entryObject->codeSize()); |
currentSize+=_entryObject->codeSize(); |
// Write other objects |
for(auto const &obj: _objects) { |
if(obj==_entryObject) continue; |
writer.write(reinterpret_cast<const char*>(obj->code()),obj->codeSize()); |
currentSize+=obj->codeSize(); |
} |
|
// Pad file if requested |
if(_imageSize>0) { |
if(currentSize>_imageSize) |
throw std::runtime_error("Image size exceeds the specified value"); |
else if(currentSize<_imageSize) writer.pad(_imageSize-currentSize); |
} |
} |
|
void Linker::markAsUsed(const LinkableObject *obj,std::set<const LinkableObject*> &used) { |
if(used.find(obj)!=used.end()) return; // already processed |
used.insert(obj); |
for(auto const &sym: _globalSymbolTable) { |
for(auto const &ref: sym.second.refs) { |
if(ref==obj) markAsUsed(sym.second.obj,used); |
} |
} |
} |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Linker class. |
*/ |
|
#include "linker.h" |
|
#include "linkableobject.h" |
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
#include <sstream> |
#include <map> |
#include <stdexcept> |
#include <cassert> |
#include <algorithm> |
|
void Linker::addObject(LinkableObject &obj) { |
_objects.push_back(&obj); |
} |
|
void Linker::link(OutputWriter &writer) { |
if(_objects.empty()) throw std::runtime_error("Object set is empty"); |
|
// Merge symbol tables |
buildSymbolTable(); |
|
// Determine entry point |
if(_objects.size()==1) _entryObject=_objects[0]; |
else if(_entryObject==nullptr) |
throw std::runtime_error("Entry point not defined: cannot find \"entry\" or \"Entry\" symbol"); |
|
// Assign virtual addresses |
placeObjects(); |
|
// Perform relocations |
for(auto &obj: _objects) relocateObject(obj); |
|
// Write binary data |
writeObjects(writer); |
_bytesWritten=writer.size(); |
} |
|
void Linker::setBase(LinkableObject::Word base) { |
_base=base; |
} |
|
void Linker::setAlignment(std::size_t align) { |
_align=align; |
} |
|
void Linker::setImageSize(std::size_t size) { |
_imageSize=size; |
} |
|
void Linker::generateMap(std::ostream &s) { |
// Calculate the maximum length of a symbol name |
std::size_t len=0; |
for(auto const &obj: _objects) { |
for(auto const &sym: obj->symbols()) { |
if(sym.second.type!=LinkableObject::Imported) |
len=std::max(len,sym.first.size()); |
} |
} |
len=std::max(len+3,std::size_t(8)); // width of the first column |
|
s<<"Image base address: "<<Utils::hex(_base)<<std::endl; |
s<<"Object alignment: "<<_align<<std::endl; |
s<<"Image size: "<<(_bytesWritten/4)<<" words"<<std::endl; |
s<<"Number of objects: "<<_objects.size()<<std::endl; |
s<<std::endl; |
|
for(auto const &obj: _objects) { |
s<<"Object \""<<obj->name()<<"\" at address "<<Utils::hex(obj->virtualAddress())<<std::endl; |
s<<std::endl; |
std::multimap<LinkableObject::Word,std::pair<std::string,LinkableObject::SymbolData> > sorted; |
for(auto const &sym: obj->symbols()) sorted.emplace(sym.second.rva,sym); |
for(auto const &sym: sorted) { |
if(sym.second.second.type==LinkableObject::Imported) continue; |
s<<sym.second.first; |
s<<std::string(len-sym.second.first.size(),' '); |
s<<Utils::hex(obj->virtualAddress()+sym.second.second.rva); |
if(sym.second.second.type==LinkableObject::Local) s<<" Local"; |
else s<<" Exported"; |
s<<std::endl; |
} |
s<<std::endl; |
} |
} |
|
/* |
* Private members |
*/ |
|
void Linker::buildSymbolTable() { |
_globalSymbolTable.clear(); |
|
// Build a table of exported symbols from all modules |
for(auto const &obj: _objects) { |
auto const &table=obj->symbols(); |
for(auto const &item: table) { |
if((item.first=="entry"||item.first=="Entry")&&item.second.type!=LinkableObject::Imported) { |
if(_entryObject) { |
std::ostringstream msg; |
msg<<obj->name()<<": Duplicate definition of the entry symbol "; |
msg<<"(previously defined in "<<_entryObject->name()<<")"; |
throw std::runtime_error(msg.str()); |
} |
if(item.second.rva!=0) { |
std::ostringstream msg; |
msg<<obj->name()<<": "; |
msg<<"Entry point must refer to the start of the object"; |
throw std::runtime_error(msg.str()); |
} |
_entryObject=obj; |
} |
if(item.second.type==LinkableObject::Local) continue; |
// Insert item to the global symbol table if it doesn't exist yet |
auto it=_globalSymbolTable.emplace(item.first,GlobalSymbolData()).first; |
|
// Check that the symbol has not been already defined in another object |
if(item.second.type==LinkableObject::Exported) { |
if(it->second.obj) { |
std::ostringstream msg; |
msg<<obj->name()<<": Duplicate definition of \""<<item.first; |
msg<<"\" (previously defined in "<<it->second.obj->name()<<")"; |
throw std::runtime_error(msg.str()); |
} |
it->second.obj=obj; |
it->second.rva=item.second.rva; |
} |
|
if(!item.second.refs.empty()) it->second.refs.insert(obj); |
} |
} |
|
// Check that local symbols don't shadow the public ones |
for(auto const &obj: _objects) { |
auto const &table=obj->symbols(); |
for(auto const &item: table) { |
if(item.second.type!=LinkableObject::Local) continue; |
auto it=_globalSymbolTable.find(item.first); |
if(it==_globalSymbolTable.end()) continue; |
if(!it->second.obj) continue; |
if(item.first==it->first) { |
std::ostringstream msg; |
msg<<obj->name()<<": Local symbol \""<<item.first<<"\" shadows the public one "; |
msg<<"(defined in "<<it->second.obj->name()<<")"; |
throw std::runtime_error(msg.str()); |
} |
} |
} |
|
// Check that no undefined symbols remain |
for(auto const &item: _globalSymbolTable) { |
if(item.second.obj==nullptr&&!item.second.refs.empty()) { |
std::ostringstream msg; |
msg<<"Undefined symbol: \""<<item.first<<"\""; |
auto const it=item.second.refs.begin(); |
msg<<" (referenced from "<<(*it)->name()<<")"; |
throw std::runtime_error(msg.str()); |
} |
} |
} |
|
void Linker::placeObjects() { |
auto currentBase=_base; |
|
// Make entry object the first |
if(_objects.size()>1) { |
for(auto it=_objects.begin();it!=_objects.end();++it) { |
if(*it==_entryObject) { |
_objects.erase(it); |
break; |
} |
} |
_objects.insert(_objects.begin(),_entryObject); |
} |
|
// Remove unreferenced objects |
if(_objects.size()>1) { |
std::set<const LinkableObject*> used; |
markAsUsed(_objects[0],used); |
for(auto it=_objects.begin();it!=_objects.end();) { |
if(used.find(*it)==used.end()) { |
std::cerr<<"Linker warning: skipping an unreferenced object \""; |
std::cerr<<(*it)->name()<<"\""<<std::endl; |
for(auto sym=_globalSymbolTable.begin();sym!=_globalSymbolTable.end();) { |
if(sym->second.obj==*it) sym=_globalSymbolTable.erase(sym); |
else ++sym; |
} |
it=_objects.erase(it); |
} |
else ++it; |
} |
} |
|
// Set base addresses |
for(auto it=_objects.begin();it!=_objects.end();++it) { |
(*it)->setVirtualAddress(currentBase); |
if(it+1!=_objects.end()) (*it)->addPadding(_align); |
else (*it)->addPadding(); |
currentBase+=static_cast<LinkableObject::Word>((*it)->codeSize()); |
} |
} |
|
void Linker::relocateObject(LinkableObject *obj) { |
for(auto const &sym: obj->symbols()) { |
LinkableObject::Word addr; |
if(sym.second.refs.empty()) continue; |
|
if(sym.second.type==LinkableObject::Local) addr=obj->virtualAddress()+sym.second.rva; |
else { |
auto it=_globalSymbolTable.find(sym.first); |
assert(it!=_globalSymbolTable.end()); |
assert(it->second.obj); |
addr=it->second.obj->virtualAddress()+it->second.rva; |
} |
|
for(auto const &ref: sym.second.refs) { |
if(ref.type==LinkableObject::Regular) obj->replaceWord(ref.rva,addr+ref.offset); |
else { |
auto target=static_cast<LinkableObject::Word>(addr+ref.offset); |
if(target>0xFFFFF&&target<0xFFF00000) { |
std::ostringstream msg; |
msg<<"Address 0x"<<Utils::hex(target)<<" is out of the range for a short reference"; |
msg<<" (referenced from "<<ref.source<<":"<<ref.line<<")"; |
throw std::runtime_error(msg.str()); |
} |
target&=0x1FFFFF; |
auto w=obj->getWord(ref.rva); |
w|=(target&0xFFFF); |
w|=((target<<8)&0x1F000000); |
obj->replaceWord(ref.rva,w); |
} |
} |
} |
} |
|
void Linker::writeObjects(OutputWriter &writer) { |
std::size_t currentSize=0; |
// Write entry object |
writer.write(reinterpret_cast<const char*>(_entryObject->code()),_entryObject->codeSize()); |
currentSize+=_entryObject->codeSize(); |
// Write other objects |
for(auto const &obj: _objects) { |
if(obj==_entryObject) continue; |
writer.write(reinterpret_cast<const char*>(obj->code()),obj->codeSize()); |
currentSize+=obj->codeSize(); |
} |
|
// Pad file if requested |
if(_imageSize>0) { |
if(currentSize>_imageSize) |
throw std::runtime_error("Image size exceeds the specified value"); |
else if(currentSize<_imageSize) writer.pad(_imageSize-currentSize); |
} |
} |
|
void Linker::markAsUsed(const LinkableObject *obj,std::set<const LinkableObject*> &used) { |
if(used.find(obj)!=used.end()) return; // already processed |
used.insert(obj); |
for(auto const &sym: _globalSymbolTable) { |
for(auto const &ref: sym.second.refs) { |
if(ref==obj) markAsUsed(sym.second.obj,used); |
} |
} |
} |
/lxp32asm/linker.h
1,53 → 1,53
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Linker class which performs |
* linking of LXP32 binary objects. |
*/ |
|
#ifndef LINKER_H_INCLUDED |
#define LINKER_H_INCLUDED |
|
#include "linkableobject.h" |
#include "outputwriter.h" |
|
#include <iostream> |
#include <map> |
#include <vector> |
#include <string> |
#include <set> |
|
class Linker { |
struct GlobalSymbolData { |
LinkableObject *obj=nullptr; |
LinkableObject::Word rva=0; |
std::set<const LinkableObject*> refs; |
}; |
|
std::vector<LinkableObject*> _objects; |
LinkableObject *_entryObject=nullptr; |
std::map<std::string,GlobalSymbolData> _globalSymbolTable; |
|
// Various output options |
LinkableObject::Word _base=0; |
std::size_t _align=4; |
std::size_t _imageSize=0; |
std::size_t _bytesWritten=0; |
public: |
void addObject(LinkableObject &obj); |
void link(OutputWriter &writer); |
void setBase(LinkableObject::Word base); |
void setAlignment(std::size_t align); |
void setImageSize(std::size_t size); |
void generateMap(std::ostream &s); |
private: |
void buildSymbolTable(); |
void placeObjects(); |
void relocateObject(LinkableObject *obj); |
void writeObjects(OutputWriter &writer); |
void markAsUsed(const LinkableObject *obj,std::set<const LinkableObject*> &used); |
}; |
|
#endif |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Linker class which performs |
* linking of LXP32 binary objects. |
*/ |
|
#ifndef LINKER_H_INCLUDED |
#define LINKER_H_INCLUDED |
|
#include "linkableobject.h" |
#include "outputwriter.h" |
|
#include <iostream> |
#include <map> |
#include <vector> |
#include <string> |
#include <set> |
|
class Linker { |
struct GlobalSymbolData { |
LinkableObject *obj=nullptr; |
LinkableObject::Word rva=0; |
std::set<const LinkableObject*> refs; |
}; |
|
std::vector<LinkableObject*> _objects; |
LinkableObject *_entryObject=nullptr; |
std::map<std::string,GlobalSymbolData> _globalSymbolTable; |
|
// Various output options |
LinkableObject::Word _base=0; |
std::size_t _align=4; |
std::size_t _imageSize=0; |
std::size_t _bytesWritten=0; |
public: |
void addObject(LinkableObject &obj); |
void link(OutputWriter &writer); |
void setBase(LinkableObject::Word base); |
void setAlignment(std::size_t align); |
void setImageSize(std::size_t size); |
void generateMap(std::ostream &s); |
private: |
void buildSymbolTable(); |
void placeObjects(); |
void relocateObject(LinkableObject *obj); |
void writeObjects(OutputWriter &writer); |
void markAsUsed(const LinkableObject *obj,std::set<const LinkableObject*> &used); |
}; |
|
#endif |
/lxp32asm/main.cpp
1,308 → 1,308
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* Main translation unit for the LXP32 assembler/linker. |
*/ |
|
#include "assembler.h" |
#include "linker.h" |
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
#include <vector> |
#include <string> |
#include <exception> |
#include <utility> |
#include <memory> |
#include <cstdlib> |
#include <cstring> |
#include <cassert> |
|
struct Options { |
enum OutputFormat {Bin,Textio,Dec,Hex}; |
|
bool compileOnly=false; |
std::string outputFileName; |
std::string mapFileName; |
std::vector<std::string> includeSearchDirs; |
LinkableObject::Word base=0; |
std::size_t align=4; |
std::size_t imageSize=0; |
OutputFormat fmt=Bin; |
}; |
|
static void displayUsage(std::ostream &os,const char *program) { |
os<<std::endl; |
os<<"Usage:"<<std::endl; |
os<<" "<<program<<" [ option(s) | input file(s) ]"<<std::endl<<std::endl; |
|
os<<"Options:"<<std::endl; |
os<<" -a <align> Object alignment (default: 4)"<<std::endl; |
os<<" -b <addr> Base address (default: 0)"<<std::endl; |
os<<" -c Compile only (don't link)"<<std::endl; |
os<<" -f <fmt> Output file format (see below)"<<std::endl; |
os<<" -h, --help Display a short help message"<<std::endl; |
os<<" -i <dir> Add directory to the list of directories used to search"<<std::endl; |
os<<" for included files (multiple directories can be specified)"<<std::endl; |
os<<" -m <file> Generate map file"<<std::endl; |
os<<" -o <file> Output file name"<<std::endl; |
os<<" -s <size> Output image size"<<std::endl; |
os<<" -- Do not interpret subsequent arguments as options"<<std::endl; |
os<<std::endl; |
os<<"Object alignment must be a power of two and can't be less than 4."<<std::endl; |
os<<"Base address must be a multiple of object alignment."<<std::endl; |
os<<"Image size must be a multiple of 4."<<std::endl; |
os<<std::endl; |
|
os<<"Output file formats:"<<std::endl; |
os<<" bin Raw binary image (default)"<<std::endl; |
os<<" textio Text representation of binary data. Supported by"<<std::endl; |
os<<" std.textio (VHDL) and $readmemb (Verilog)"<<std::endl; |
os<<" dec Text format, one word per line (decimal)"<<std::endl; |
os<<" hex Text format, one word per line (hexadecimal)"<<std::endl; |
} |
|
static bool isLinkableObject(const std::string &filename) { |
static const char *id="LinkableObject"; |
static std::size_t idSize=std::strlen(id); |
|
std::ifstream in(filename,std::ios_base::in); |
if(!in) return false; |
if(in.tellg()==static_cast<std::ifstream::pos_type>(-1)) |
return false; // the stream is not seekable |
|
std::vector<char> buf(idSize); |
in.read(buf.data(),idSize); |
if(static_cast<std::size_t>(in.gcount())!=idSize) return false; |
if(std::memcmp(buf.data(),id,idSize)) return false; |
return true; |
} |
|
int main(int argc,char *argv[]) try { |
std::vector<std::string> inputFiles; |
Options options; |
bool alignmentSpecified=false; |
bool baseSpecified=false; |
bool formatSpecified=false; |
bool noMoreOptions=false; |
|
std::cout<<"LXP32 Platform Assembler and Linker"<<std::endl; |
std::cout<<"Copyright (c) 2016-2019 by Alex I. Kuznetsov"<<std::endl; |
|
if(argc<=1) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
|
for(int i=1;i<argc;i++) { |
if(argv[i][0]!='-'||noMoreOptions) inputFiles.push_back(argv[i]); |
else if(!strcmp(argv[i],"--")) noMoreOptions=true; |
else if(!strcmp(argv[i],"-a")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
try { |
options.align=std::stoul(argv[i],nullptr,0); |
if(!Utils::isPowerOf2(options.align)) throw std::exception(); |
if(options.align<4) throw std::exception(); |
alignmentSpecified=true; |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid object alignment"); |
} |
} |
else if(!strcmp(argv[i],"-b")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
try { |
options.base=std::stoul(argv[i],nullptr,0); |
baseSpecified=true; |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid base address"); |
} |
} |
else if(!strcmp(argv[i],"-c")) { |
options.compileOnly=true; |
} |
else if(!strcmp(argv[i],"-f")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
if(!strcmp(argv[i],"bin")) options.fmt=Options::Bin; |
else if(!strcmp(argv[i],"textio")) options.fmt=Options::Textio; |
else if(!strcmp(argv[i],"dec")) options.fmt=Options::Dec; |
else if(!strcmp(argv[i],"hex")) options.fmt=Options::Hex; |
else throw std::runtime_error("Unrecognized output format"); |
formatSpecified=true; |
} |
else if(!strcmp(argv[i],"-h")||!strcmp(argv[i],"--help")) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
else if(!strcmp(argv[i],"-i")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
options.includeSearchDirs.push_back(argv[i]); |
} |
else if(!strcmp(argv[i],"-m")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
options.mapFileName=argv[i]; |
} |
else if(!strcmp(argv[i],"-o")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
options.outputFileName=argv[i]; |
} |
else if(!strcmp(argv[i],"-s")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
try { |
options.imageSize=std::stoul(argv[i],nullptr,0); |
if(options.imageSize%4!=0||options.imageSize==0) throw std::exception(); |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid image size"); |
} |
} |
else throw std::runtime_error(std::string("Unrecognized option: \"")+argv[i]+"\""); |
} |
|
if(options.base%options.align!=0) |
throw std::runtime_error("Base address must be a multiple of object alignment"); |
|
if(options.compileOnly) { |
if(alignmentSpecified) |
std::cerr<<"Warning: Object alignment is ignored in compile-only mode"<<std::endl; |
if(baseSpecified) |
std::cerr<<"Warning: Base address is ignored in compile-only mode"<<std::endl; |
if(formatSpecified) |
std::cerr<<"Warning: Output format is ignored in compile-only mode"<<std::endl; |
if(options.imageSize>0) |
std::cerr<<"Warning: Image size is ignored in compile-only mode"<<std::endl; |
if(!options.mapFileName.empty()) |
std::cerr<<"Warning: Map file is not generated in compile-only mode"<<std::endl; |
} |
|
if(inputFiles.empty()) |
throw std::runtime_error("No input files were specified"); |
|
if(options.compileOnly&&inputFiles.size()>1&&!options.outputFileName.empty()) |
throw std::runtime_error("Output file name cannot be specified " |
"for multiple files in compile-only mode"); |
|
std::vector<Assembler> assemblers; |
std::vector<LinkableObject> rawObjects; |
|
for(auto const &filename: inputFiles) { |
if(options.compileOnly||!isLinkableObject(filename)) { |
Assembler as; |
for(auto const &dir: options.includeSearchDirs) as.addIncludeSearchDir(dir); |
try { |
as.processFile(filename); |
} |
catch(std::exception &ex) { |
std::cerr<<"Assembler error in "<<as.currentFileName(); |
if(as.line()>0) std::cerr<<":"<<as.line(); |
std::cerr<<": "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
if(!options.compileOnly) assemblers.push_back(std::move(as)); |
else { |
std::string outputFileName=options.outputFileName; |
if(outputFileName.empty()) { |
outputFileName=filename; |
auto pos=outputFileName.find_last_of('.'); |
if(pos!=std::string::npos) outputFileName.erase(pos); |
outputFileName+=".lo"; |
} |
as.object().serialize(outputFileName); |
} |
} |
else { |
LinkableObject lo; |
try { |
lo.deserialize(filename); |
} |
catch(std::exception &ex) { |
std::cerr<<"Error reading object file "<<filename<<": "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
rawObjects.push_back(std::move(lo)); |
} |
} |
|
if(options.compileOnly) return 0; |
|
Linker linker; |
for(auto &lo: rawObjects) linker.addObject(lo); |
for(auto &as: assemblers) linker.addObject(as.object()); |
linker.setBase(options.base); |
linker.setAlignment(options.align); |
linker.setImageSize(options.imageSize); |
|
std::string outputFileName=options.outputFileName; |
if(outputFileName.empty()) { |
outputFileName=inputFiles[0]; |
auto pos=outputFileName.find_last_of('.'); |
if(pos!=std::string::npos) outputFileName.erase(pos); |
if(options.fmt==Options::Bin) outputFileName+=".bin"; |
else outputFileName+=".txt"; |
} |
|
std::unique_ptr<OutputWriter> writer; |
|
switch(options.fmt) { |
case Options::Bin: |
writer=std::unique_ptr<OutputWriter>(new BinaryOutputWriter(outputFileName)); |
break; |
case Options::Textio: |
writer=std::unique_ptr<OutputWriter>(new TextOutputWriter(outputFileName,TextOutputWriter::Bin)); |
break; |
case Options::Dec: |
writer=std::unique_ptr<OutputWriter>(new TextOutputWriter(outputFileName,TextOutputWriter::Dec)); |
break; |
case Options::Hex: |
writer=std::unique_ptr<OutputWriter>(new TextOutputWriter(outputFileName,TextOutputWriter::Hex)); |
break; |
default: |
assert(false); |
} |
|
try { |
linker.link(*writer); |
} |
catch(std::exception &ex) { |
writer->abort(); |
std::cerr<<"Linker error: "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
|
std::cout<<writer->size()/4<<" words written"<<std::endl; |
|
if(!options.mapFileName.empty()) { |
std::ofstream out(options.mapFileName); |
if(!out) throw std::runtime_error("Cannot open file \""+options.mapFileName+"\" for writing"); |
linker.generateMap(out); |
} |
} |
catch(std::exception &ex) { |
std::cerr<<"Error: "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* Main translation unit for the LXP32 assembler/linker. |
*/ |
|
#include "assembler.h" |
#include "linker.h" |
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
#include <vector> |
#include <string> |
#include <exception> |
#include <utility> |
#include <memory> |
#include <cstdlib> |
#include <cstring> |
#include <cassert> |
|
struct Options { |
enum OutputFormat {Bin,Textio,Dec,Hex}; |
|
bool compileOnly=false; |
std::string outputFileName; |
std::string mapFileName; |
std::vector<std::string> includeSearchDirs; |
LinkableObject::Word base=0; |
std::size_t align=4; |
std::size_t imageSize=0; |
OutputFormat fmt=Bin; |
}; |
|
static void displayUsage(std::ostream &os,const char *program) { |
os<<std::endl; |
os<<"Usage:"<<std::endl; |
os<<" "<<program<<" [ option(s) | input file(s) ]"<<std::endl<<std::endl; |
|
os<<"Options:"<<std::endl; |
os<<" -a <align> Object alignment (default: 4)"<<std::endl; |
os<<" -b <addr> Base address (default: 0)"<<std::endl; |
os<<" -c Compile only (don't link)"<<std::endl; |
os<<" -f <fmt> Output file format (see below)"<<std::endl; |
os<<" -h, --help Display a short help message"<<std::endl; |
os<<" -i <dir> Add directory to the list of directories used to search"<<std::endl; |
os<<" for included files (multiple directories can be specified)"<<std::endl; |
os<<" -m <file> Generate map file"<<std::endl; |
os<<" -o <file> Output file name"<<std::endl; |
os<<" -s <size> Output image size"<<std::endl; |
os<<" -- Do not interpret subsequent arguments as options"<<std::endl; |
os<<std::endl; |
os<<"Object alignment must be a power of two and can't be less than 4."<<std::endl; |
os<<"Base address must be a multiple of object alignment."<<std::endl; |
os<<"Image size must be a multiple of 4."<<std::endl; |
os<<std::endl; |
|
os<<"Output file formats:"<<std::endl; |
os<<" bin Raw binary image (default)"<<std::endl; |
os<<" textio Text representation of binary data. Supported by"<<std::endl; |
os<<" std.textio (VHDL) and $readmemb (Verilog)"<<std::endl; |
os<<" dec Text format, one word per line (decimal)"<<std::endl; |
os<<" hex Text format, one word per line (hexadecimal)"<<std::endl; |
} |
|
static bool isLinkableObject(const std::string &filename) { |
static const char *id="LinkableObject"; |
static std::size_t idSize=std::strlen(id); |
|
std::ifstream in(filename,std::ios_base::in); |
if(!in) return false; |
if(in.tellg()==static_cast<std::ifstream::pos_type>(-1)) |
return false; // the stream is not seekable |
|
std::vector<char> buf(idSize); |
in.read(buf.data(),idSize); |
if(static_cast<std::size_t>(in.gcount())!=idSize) return false; |
if(std::memcmp(buf.data(),id,idSize)) return false; |
return true; |
} |
|
int main(int argc,char *argv[]) try { |
std::vector<std::string> inputFiles; |
Options options; |
bool alignmentSpecified=false; |
bool baseSpecified=false; |
bool formatSpecified=false; |
bool noMoreOptions=false; |
|
std::cout<<"LXP32 Platform Assembler and Linker"<<std::endl; |
std::cout<<"Copyright (c) 2016-2019 by Alex I. Kuznetsov"<<std::endl; |
|
if(argc<=1) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
|
for(int i=1;i<argc;i++) { |
if(argv[i][0]!='-'||noMoreOptions) inputFiles.push_back(argv[i]); |
else if(!strcmp(argv[i],"--")) noMoreOptions=true; |
else if(!strcmp(argv[i],"-a")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
try { |
options.align=std::stoul(argv[i],nullptr,0); |
if(!Utils::isPowerOf2(options.align)) throw std::exception(); |
if(options.align<4) throw std::exception(); |
alignmentSpecified=true; |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid object alignment"); |
} |
} |
else if(!strcmp(argv[i],"-b")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
try { |
options.base=std::stoul(argv[i],nullptr,0); |
baseSpecified=true; |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid base address"); |
} |
} |
else if(!strcmp(argv[i],"-c")) { |
options.compileOnly=true; |
} |
else if(!strcmp(argv[i],"-f")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
if(!strcmp(argv[i],"bin")) options.fmt=Options::Bin; |
else if(!strcmp(argv[i],"textio")) options.fmt=Options::Textio; |
else if(!strcmp(argv[i],"dec")) options.fmt=Options::Dec; |
else if(!strcmp(argv[i],"hex")) options.fmt=Options::Hex; |
else throw std::runtime_error("Unrecognized output format"); |
formatSpecified=true; |
} |
else if(!strcmp(argv[i],"-h")||!strcmp(argv[i],"--help")) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
else if(!strcmp(argv[i],"-i")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
options.includeSearchDirs.push_back(argv[i]); |
} |
else if(!strcmp(argv[i],"-m")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
options.mapFileName=argv[i]; |
} |
else if(!strcmp(argv[i],"-o")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
options.outputFileName=argv[i]; |
} |
else if(!strcmp(argv[i],"-s")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
try { |
options.imageSize=std::stoul(argv[i],nullptr,0); |
if(options.imageSize%4!=0||options.imageSize==0) throw std::exception(); |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid image size"); |
} |
} |
else throw std::runtime_error(std::string("Unrecognized option: \"")+argv[i]+"\""); |
} |
|
if(options.base%options.align!=0) |
throw std::runtime_error("Base address must be a multiple of object alignment"); |
|
if(options.compileOnly) { |
if(alignmentSpecified) |
std::cerr<<"Warning: Object alignment is ignored in compile-only mode"<<std::endl; |
if(baseSpecified) |
std::cerr<<"Warning: Base address is ignored in compile-only mode"<<std::endl; |
if(formatSpecified) |
std::cerr<<"Warning: Output format is ignored in compile-only mode"<<std::endl; |
if(options.imageSize>0) |
std::cerr<<"Warning: Image size is ignored in compile-only mode"<<std::endl; |
if(!options.mapFileName.empty()) |
std::cerr<<"Warning: Map file is not generated in compile-only mode"<<std::endl; |
} |
|
if(inputFiles.empty()) |
throw std::runtime_error("No input files were specified"); |
|
if(options.compileOnly&&inputFiles.size()>1&&!options.outputFileName.empty()) |
throw std::runtime_error("Output file name cannot be specified " |
"for multiple files in compile-only mode"); |
|
std::vector<Assembler> assemblers; |
std::vector<LinkableObject> rawObjects; |
|
for(auto const &filename: inputFiles) { |
if(options.compileOnly||!isLinkableObject(filename)) { |
Assembler as; |
for(auto const &dir: options.includeSearchDirs) as.addIncludeSearchDir(dir); |
try { |
as.processFile(filename); |
} |
catch(std::exception &ex) { |
std::cerr<<"Assembler error in "<<as.currentFileName(); |
if(as.line()>0) std::cerr<<":"<<as.line(); |
std::cerr<<": "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
if(!options.compileOnly) assemblers.push_back(std::move(as)); |
else { |
std::string outputFileName=options.outputFileName; |
if(outputFileName.empty()) { |
outputFileName=filename; |
auto pos=outputFileName.find_last_of('.'); |
if(pos!=std::string::npos) outputFileName.erase(pos); |
outputFileName+=".lo"; |
} |
as.object().serialize(outputFileName); |
} |
} |
else { |
LinkableObject lo; |
try { |
lo.deserialize(filename); |
} |
catch(std::exception &ex) { |
std::cerr<<"Error reading object file "<<filename<<": "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
rawObjects.push_back(std::move(lo)); |
} |
} |
|
if(options.compileOnly) return 0; |
|
Linker linker; |
for(auto &lo: rawObjects) linker.addObject(lo); |
for(auto &as: assemblers) linker.addObject(as.object()); |
linker.setBase(options.base); |
linker.setAlignment(options.align); |
linker.setImageSize(options.imageSize); |
|
std::string outputFileName=options.outputFileName; |
if(outputFileName.empty()) { |
outputFileName=inputFiles[0]; |
auto pos=outputFileName.find_last_of('.'); |
if(pos!=std::string::npos) outputFileName.erase(pos); |
if(options.fmt==Options::Bin) outputFileName+=".bin"; |
else outputFileName+=".txt"; |
} |
|
std::unique_ptr<OutputWriter> writer; |
|
switch(options.fmt) { |
case Options::Bin: |
writer=std::unique_ptr<OutputWriter>(new BinaryOutputWriter(outputFileName)); |
break; |
case Options::Textio: |
writer=std::unique_ptr<OutputWriter>(new TextOutputWriter(outputFileName,TextOutputWriter::Bin)); |
break; |
case Options::Dec: |
writer=std::unique_ptr<OutputWriter>(new TextOutputWriter(outputFileName,TextOutputWriter::Dec)); |
break; |
case Options::Hex: |
writer=std::unique_ptr<OutputWriter>(new TextOutputWriter(outputFileName,TextOutputWriter::Hex)); |
break; |
default: |
assert(false); |
} |
|
try { |
linker.link(*writer); |
} |
catch(std::exception &ex) { |
writer->abort(); |
std::cerr<<"Linker error: "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
|
std::cout<<writer->size()/4<<" words written"<<std::endl; |
|
if(!options.mapFileName.empty()) { |
std::ofstream out(options.mapFileName); |
if(!out) throw std::runtime_error("Cannot open file \""+options.mapFileName+"\" for writing"); |
linker.generateMap(out); |
} |
} |
catch(std::exception &ex) { |
std::cerr<<"Error: "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
/lxp32asm/outputwriter.cpp
1,109 → 1,109
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the OutputWriter class |
* and its derived classes. |
*/ |
|
#include "outputwriter.h" |
#include "utils.h" |
|
#include <iostream> |
#include <iomanip> |
#include <stdexcept> |
#include <algorithm> |
#include <cassert> |
#include <cstdint> |
#include <cstdio> |
|
/* |
* OutputWriter members |
*/ |
|
void OutputWriter::write(const char *data,std::size_t n) { |
writeData(data,n); |
_size+=n; |
} |
|
void OutputWriter::pad(std::size_t size) { |
static char zeros[256]; // static objects are zero-initialized |
while(size>0) { |
auto n=std::min<std::size_t>(size,256); |
write(zeros,n); |
size-=n; |
} |
} |
|
std::size_t OutputWriter::size() const { |
return _size; |
} |
|
/* |
* BinaryOutputWriter members |
*/ |
|
BinaryOutputWriter::BinaryOutputWriter(const std::string &filename): |
_filename(filename), |
_os(filename,std::ios_base::out|std::ios_base::binary) |
{ |
if(!_os) throw std::runtime_error("Cannot open \""+filename+"\" for writing"); |
} |
|
void BinaryOutputWriter::writeData(const char *data,std::size_t n) { |
_os.write(data,n); |
} |
|
void BinaryOutputWriter::abort() { |
_os.close(); |
std::remove(_filename.c_str()); |
} |
|
/* |
* TextOutputWriter members |
*/ |
|
TextOutputWriter::TextOutputWriter(const std::string &filename,Format f): |
_filename(filename), |
_os(filename,std::ios_base::out), |
_fmt(f) |
{ |
if(!_os) throw std::runtime_error("Cannot open \""+filename+"\" for writing"); |
} |
|
TextOutputWriter::~TextOutputWriter() { |
if(!_buf.empty()) { |
assert(_buf.size()<4); |
pad(4-_buf.size()); |
} |
} |
|
void TextOutputWriter::writeData(const char *data,std::size_t n) { |
while(n>0) { |
assert(_buf.size()<4); |
auto count=std::min(4-_buf.size(),n); |
_buf.append(data,count); |
data+=count; |
n-=count; |
|
if(_buf.size()<4) continue; |
|
assert(_buf.size()==4); |
|
std::uint32_t word=(static_cast<unsigned char>(_buf[3])<<24)| |
(static_cast<unsigned char>(_buf[2])<<16)| |
(static_cast<unsigned char>(_buf[1])<<8)| |
static_cast<unsigned char>(_buf[0]); |
|
if(_fmt==Bin) _os<<Utils::bin(word)<<std::endl; |
else if(_fmt==Dec) _os<<word<<std::endl; |
else _os<<Utils::hex(word)<<std::endl; |
_buf.clear(); |
} |
} |
|
void TextOutputWriter::abort() { |
_os.close(); |
std::remove(_filename.c_str()); |
} |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the OutputWriter class |
* and its derived classes. |
*/ |
|
#include "outputwriter.h" |
#include "utils.h" |
|
#include <iostream> |
#include <iomanip> |
#include <stdexcept> |
#include <algorithm> |
#include <cassert> |
#include <cstdint> |
#include <cstdio> |
|
/* |
* OutputWriter members |
*/ |
|
void OutputWriter::write(const char *data,std::size_t n) { |
writeData(data,n); |
_size+=n; |
} |
|
void OutputWriter::pad(std::size_t size) { |
static char zeros[256]; // static objects are zero-initialized |
while(size>0) { |
auto n=std::min<std::size_t>(size,256); |
write(zeros,n); |
size-=n; |
} |
} |
|
std::size_t OutputWriter::size() const { |
return _size; |
} |
|
/* |
* BinaryOutputWriter members |
*/ |
|
BinaryOutputWriter::BinaryOutputWriter(const std::string &filename): |
_filename(filename), |
_os(filename,std::ios_base::out|std::ios_base::binary) |
{ |
if(!_os) throw std::runtime_error("Cannot open \""+filename+"\" for writing"); |
} |
|
void BinaryOutputWriter::writeData(const char *data,std::size_t n) { |
_os.write(data,n); |
} |
|
void BinaryOutputWriter::abort() { |
_os.close(); |
std::remove(_filename.c_str()); |
} |
|
/* |
* TextOutputWriter members |
*/ |
|
TextOutputWriter::TextOutputWriter(const std::string &filename,Format f): |
_filename(filename), |
_os(filename,std::ios_base::out), |
_fmt(f) |
{ |
if(!_os) throw std::runtime_error("Cannot open \""+filename+"\" for writing"); |
} |
|
TextOutputWriter::~TextOutputWriter() { |
if(!_buf.empty()) { |
assert(_buf.size()<4); |
pad(4-_buf.size()); |
} |
} |
|
void TextOutputWriter::writeData(const char *data,std::size_t n) { |
while(n>0) { |
assert(_buf.size()<4); |
auto count=std::min(4-_buf.size(),n); |
_buf.append(data,count); |
data+=count; |
n-=count; |
|
if(_buf.size()<4) continue; |
|
assert(_buf.size()==4); |
|
std::uint32_t word=(static_cast<unsigned char>(_buf[3])<<24)| |
(static_cast<unsigned char>(_buf[2])<<16)| |
(static_cast<unsigned char>(_buf[1])<<8)| |
static_cast<unsigned char>(_buf[0]); |
|
if(_fmt==Bin) _os<<Utils::bin(word)<<std::endl; |
else if(_fmt==Dec) _os<<word<<std::endl; |
else _os<<Utils::hex(word)<<std::endl; |
_buf.clear(); |
} |
} |
|
void TextOutputWriter::abort() { |
_os.close(); |
std::remove(_filename.c_str()); |
} |
/lxp32asm/outputwriter.h
1,67 → 1,67
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the OutputWriter abstract class and its |
* derived classes. These classes are used to write LXP32 executable |
* code in different formats. |
*/ |
|
#ifndef OUTPUTWRITER_H_INCLUDED |
#define OUTPUTWRITER_H_INCLUDED |
|
#include <fstream> |
#include <string> |
|
/* |
* An abstract base class for all writers |
*/ |
|
class OutputWriter { |
std::size_t _size=0; |
public: |
virtual ~OutputWriter() {} |
virtual void write(const char *data,std::size_t n); |
virtual void abort() {} |
void pad(std::size_t size); |
std::size_t size() const; |
protected: |
virtual void writeData(const char *data,std::size_t n)=0; |
}; |
|
/* |
* Write a regular binary file |
*/ |
|
class BinaryOutputWriter : public OutputWriter { |
std::string _filename; |
std::ofstream _os; |
public: |
BinaryOutputWriter(const std::string &filename); |
virtual void abort() override; |
protected: |
virtual void writeData(const char *data,std::size_t n) override; |
}; |
|
/* |
* Write a text file (one word per line) |
*/ |
|
class TextOutputWriter : public OutputWriter { |
public: |
enum Format {Bin,Dec,Hex}; |
private: |
std::string _filename; |
std::ofstream _os; |
std::string _buf; |
Format _fmt; |
public: |
TextOutputWriter(const std::string &filename,Format f); |
~TextOutputWriter(); |
virtual void abort() override; |
protected: |
virtual void writeData(const char *data,std::size_t n) override; |
}; |
|
#endif |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the OutputWriter abstract class and its |
* derived classes. These classes are used to write LXP32 executable |
* code in different formats. |
*/ |
|
#ifndef OUTPUTWRITER_H_INCLUDED |
#define OUTPUTWRITER_H_INCLUDED |
|
#include <fstream> |
#include <string> |
|
/* |
* An abstract base class for all writers |
*/ |
|
class OutputWriter { |
std::size_t _size=0; |
public: |
virtual ~OutputWriter() {} |
virtual void write(const char *data,std::size_t n); |
virtual void abort() {} |
void pad(std::size_t size); |
std::size_t size() const; |
protected: |
virtual void writeData(const char *data,std::size_t n)=0; |
}; |
|
/* |
* Write a regular binary file |
*/ |
|
class BinaryOutputWriter : public OutputWriter { |
std::string _filename; |
std::ofstream _os; |
public: |
BinaryOutputWriter(const std::string &filename); |
virtual void abort() override; |
protected: |
virtual void writeData(const char *data,std::size_t n) override; |
}; |
|
/* |
* Write a text file (one word per line) |
*/ |
|
class TextOutputWriter : public OutputWriter { |
public: |
enum Format {Bin,Dec,Hex}; |
private: |
std::string _filename; |
std::ofstream _os; |
std::string _buf; |
Format _fmt; |
public: |
TextOutputWriter(const std::string &filename,Format f); |
~TextOutputWriter(); |
virtual void abort() override; |
protected: |
virtual void writeData(const char *data,std::size_t n) override; |
}; |
|
#endif |
/lxp32asm/utils.h
1,62 → 1,62
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module declares the members of the Utils namespace. |
*/ |
|
#ifndef UTILS_H_INCLUDED |
#define UTILS_H_INCLUDED |
|
#include <string> |
#include <type_traits> |
|
namespace Utils { |
template <typename T> std::string hex(const T &w) { |
static_assert(std::is_integral<T>::value,"Argument must be of integral type"); |
const char *hexstr="0123456789ABCDEF"; |
std::string res; |
|
res.reserve(sizeof(T)*2); |
|
for(int i=sizeof(T)*8-4;i>=0;i-=4) { |
res.push_back(hexstr[(w>>i)&0x0F]); |
} |
return res; |
} |
|
template <typename T> std::string bin(const T &w) { |
static_assert(std::is_integral<T>::value,"Argument must be of integral type"); |
std::string res; |
|
res.reserve(sizeof(T)*8); |
|
for(int i=sizeof(T)*8-1;i>=0;i--) { |
if(((w>>i)&1)!=0) res.push_back('1'); |
else res.push_back('0'); |
} |
return res; |
} |
|
std::string urlEncode(const std::string &str); |
std::string urlDecode(const std::string &str); |
|
std::string normalizeSeparators(const std::string &path); |
std::string nativeSeparators(const std::string &path); |
bool isAbsolutePath(const std::string &path); |
bool fileExists(const std::string &path); |
std::string relativePath(const std::string &from,const std::string &to); |
|
std::string dequoteString(const std::string &str); |
|
bool ishexdigit(char ch); |
bool isoctdigit(char ch); |
|
template <typename T> bool isPowerOf2(const T &x) { |
static_assert(std::is_integral<T>::value,"Argument must be of integral type"); |
return (x!=0)&&((x&(x-1))==0); |
} |
} |
|
#endif |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module declares the members of the Utils namespace. |
*/ |
|
#ifndef UTILS_H_INCLUDED |
#define UTILS_H_INCLUDED |
|
#include <string> |
#include <type_traits> |
|
namespace Utils { |
template <typename T> std::string hex(const T &w) { |
static_assert(std::is_integral<T>::value,"Argument must be of integral type"); |
const char *hexstr="0123456789ABCDEF"; |
std::string res; |
|
res.reserve(sizeof(T)*2); |
|
for(int i=sizeof(T)*8-4;i>=0;i-=4) { |
res.push_back(hexstr[(w>>i)&0x0F]); |
} |
return res; |
} |
|
template <typename T> std::string bin(const T &w) { |
static_assert(std::is_integral<T>::value,"Argument must be of integral type"); |
std::string res; |
|
res.reserve(sizeof(T)*8); |
|
for(int i=sizeof(T)*8-1;i>=0;i--) { |
if(((w>>i)&1)!=0) res.push_back('1'); |
else res.push_back('0'); |
} |
return res; |
} |
|
std::string urlEncode(const std::string &str); |
std::string urlDecode(const std::string &str); |
|
std::string normalizeSeparators(const std::string &path); |
std::string nativeSeparators(const std::string &path); |
bool isAbsolutePath(const std::string &path); |
bool fileExists(const std::string &path); |
std::string relativePath(const std::string &from,const std::string &to); |
|
std::string dequoteString(const std::string &str); |
|
bool ishexdigit(char ch); |
bool isoctdigit(char ch); |
|
template <typename T> bool isPowerOf2(const T &x) { |
static_assert(std::is_integral<T>::value,"Argument must be of integral type"); |
return (x!=0)&&((x&(x-1))==0); |
} |
} |
|
#endif |
/lxp32asm/utils.cpp
1,113 → 1,113
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Utils namespace. |
*/ |
|
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
#include <algorithm> |
#include <stdexcept> |
#include <cstring> |
|
std::string Utils::urlEncode(const std::string &str) { |
std::string res; |
for(std::size_t i=0;i<str.size();i++) { |
char ch=str[i]; |
if(ch>='A'&&ch<='Z') res.push_back(ch); |
else if(ch>='a'&&ch<='z') res.push_back(ch); |
else if(ch>='0'&&ch<='9') res.push_back(ch); |
else if(ch=='-'||ch=='_'||ch=='.'||ch=='~') res.push_back(ch); |
else res+="%"+hex(ch); |
} |
return res; |
} |
|
std::string Utils::urlDecode(const std::string &str) { |
std::string res; |
for(std::size_t i=0;i<str.size();i++) { |
char ch=str[i]; |
if(ch!='%') res.push_back(ch); |
else { |
auto hexcode=str.substr(i+1,2); |
i+=hexcode.size(); |
try { |
if(hexcode.size()!=2) throw std::exception(); |
auto u=static_cast<unsigned char>(std::stoul(hexcode,nullptr,16)); |
res.push_back(static_cast<char>(u)); |
} |
catch(std::exception &) { |
throw std::runtime_error("Ill-formed URL-encoded string"); |
} |
} |
} |
return res; |
} |
|
std::string Utils::normalizeSeparators(const std::string &path) { |
std::string str(path); |
#ifdef _WIN32 |
std::replace(str.begin(),str.end(),'\\','/'); |
#endif |
return str; |
} |
|
std::string Utils::nativeSeparators(const std::string &path) { |
std::string str(path); |
#ifdef _WIN32 |
std::replace(str.begin(),str.end(),'/','\\'); |
#endif |
return str; |
} |
|
bool Utils::isAbsolutePath(const std::string &path) { |
auto native=nativeSeparators(path); |
if(native.empty()) return false; |
if(native[0]=='/') return true; |
#ifdef _WIN32 |
if(native.size()>1&&native[1]==':') return true; |
#endif |
return false; |
} |
|
bool Utils::fileExists(const std::string &path) { |
std::ifstream in(nativeSeparators(path),std::ios_base::in); |
if(!in) return false; |
return true; |
} |
|
std::string Utils::relativePath(const std::string &from,const std::string &to) { |
// Normalize directory separators |
auto nfrom=normalizeSeparators(from); |
auto nto=normalizeSeparators(to); |
|
if(nto.empty()) return std::string(); |
|
// If "nto" is an absolute path, just return it |
if(isAbsolutePath(nto)) return nativeSeparators(nto); |
|
// Process relative path |
auto pos=nfrom.find_last_of('/'); |
if(pos==std::string::npos) return nativeSeparators(nto); |
else return nativeSeparators(nfrom.substr(0,pos+1)+nto); |
} |
|
std::string Utils::dequoteString(const std::string &str) { |
if(str.size()<2) throw std::runtime_error("String literal expected"); |
if(str.front()!='\"'||str.back()!='\"') throw std::runtime_error("String literal expected"); |
return str.substr(1,str.size()-2); |
} |
|
bool Utils::ishexdigit(char ch) { |
static const char *digits="0123456789ABCDEFabcdef"; |
return (std::strchr(digits,ch)!=NULL); |
} |
|
bool Utils::isoctdigit(char ch) { |
static const char *digits="01234567"; |
return (std::strchr(digits,ch)!=NULL); |
} |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Utils namespace. |
*/ |
|
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
#include <algorithm> |
#include <stdexcept> |
#include <cstring> |
|
std::string Utils::urlEncode(const std::string &str) { |
std::string res; |
for(std::size_t i=0;i<str.size();i++) { |
char ch=str[i]; |
if(ch>='A'&&ch<='Z') res.push_back(ch); |
else if(ch>='a'&&ch<='z') res.push_back(ch); |
else if(ch>='0'&&ch<='9') res.push_back(ch); |
else if(ch=='-'||ch=='_'||ch=='.'||ch=='~') res.push_back(ch); |
else res+="%"+hex(ch); |
} |
return res; |
} |
|
std::string Utils::urlDecode(const std::string &str) { |
std::string res; |
for(std::size_t i=0;i<str.size();i++) { |
char ch=str[i]; |
if(ch!='%') res.push_back(ch); |
else { |
auto hexcode=str.substr(i+1,2); |
i+=hexcode.size(); |
try { |
if(hexcode.size()!=2) throw std::exception(); |
auto u=static_cast<unsigned char>(std::stoul(hexcode,nullptr,16)); |
res.push_back(static_cast<char>(u)); |
} |
catch(std::exception &) { |
throw std::runtime_error("Ill-formed URL-encoded string"); |
} |
} |
} |
return res; |
} |
|
std::string Utils::normalizeSeparators(const std::string &path) { |
std::string str(path); |
#ifdef _WIN32 |
std::replace(str.begin(),str.end(),'\\','/'); |
#endif |
return str; |
} |
|
std::string Utils::nativeSeparators(const std::string &path) { |
std::string str(path); |
#ifdef _WIN32 |
std::replace(str.begin(),str.end(),'/','\\'); |
#endif |
return str; |
} |
|
bool Utils::isAbsolutePath(const std::string &path) { |
auto native=nativeSeparators(path); |
if(native.empty()) return false; |
if(native[0]=='/') return true; |
#ifdef _WIN32 |
if(native.size()>1&&native[1]==':') return true; |
#endif |
return false; |
} |
|
bool Utils::fileExists(const std::string &path) { |
std::ifstream in(nativeSeparators(path),std::ios_base::in); |
if(!in) return false; |
return true; |
} |
|
std::string Utils::relativePath(const std::string &from,const std::string &to) { |
// Normalize directory separators |
auto nfrom=normalizeSeparators(from); |
auto nto=normalizeSeparators(to); |
|
if(nto.empty()) return std::string(); |
|
// If "nto" is an absolute path, just return it |
if(isAbsolutePath(nto)) return nativeSeparators(nto); |
|
// Process relative path |
auto pos=nfrom.find_last_of('/'); |
if(pos==std::string::npos) return nativeSeparators(nto); |
else return nativeSeparators(nfrom.substr(0,pos+1)+nto); |
} |
|
std::string Utils::dequoteString(const std::string &str) { |
if(str.size()<2) throw std::runtime_error("String literal expected"); |
if(str.front()!='\"'||str.back()!='\"') throw std::runtime_error("String literal expected"); |
return str.substr(1,str.size()-2); |
} |
|
bool Utils::ishexdigit(char ch) { |
static const char *digits="0123456789ABCDEFabcdef"; |
return (std::strchr(digits,ch)!=NULL); |
} |
|
bool Utils::isoctdigit(char ch) { |
static const char *digits="01234567"; |
return (std::strchr(digits,ch)!=NULL); |
} |
/lxp32asm/CMakeLists.txt
1,12 → 1,12
cmake_minimum_required(VERSION 3.3.0) |
|
add_executable(lxp32asm assembler.cpp linkableobject.cpp linker.cpp main.cpp outputwriter.cpp utils.cpp) |
|
if(MSVC) |
# Make the program expand wildcard command-line arguments |
set_target_properties(lxp32asm PROPERTIES LINK_FLAGS "setargv.obj") |
endif() |
|
# Install |
|
install(TARGETS lxp32asm DESTINATION .) |
cmake_minimum_required(VERSION 3.3.0) |
|
add_executable(lxp32asm assembler.cpp linkableobject.cpp linker.cpp main.cpp outputwriter.cpp utils.cpp) |
|
if(MSVC) |
# Make the program expand wildcard command-line arguments |
set_target_properties(lxp32asm PROPERTIES LINK_FLAGS "setargv.obj") |
endif() |
|
# Install |
|
install(TARGETS lxp32asm DESTINATION .) |
/lxp32dump/disassembler.cpp
1,456 → 1,481
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Disassembler class. |
*/ |
|
#include "disassembler.h" |
|
#include <stdexcept> |
|
/* |
* Disassembler::Operand class members |
*/ |
|
Disassembler::Operand::Operand(Type t,int value): |
_type(t),_value(value) {} |
|
Disassembler::Operand::Type Disassembler::Operand::type() const { |
return _type; |
} |
|
int Disassembler::Operand::value() const { |
return _value; |
} |
|
/* |
* Disassembler class members |
*/ |
|
Disassembler::Disassembler(std::istream &is,std::ostream &os): |
_is(is),_os(os),_fmt(Bin),_preferAliases(true),_lineNumber(0),_pos(0) {} |
|
void Disassembler::setFormat(Format fmt) { |
_fmt=fmt; |
} |
|
void Disassembler::setBase(Word base) { |
_pos=base; |
} |
|
void Disassembler::setPreferAliases(bool b) { |
_preferAliases=b; |
} |
|
void Disassembler::dump() { |
Word word; |
|
for(;;) { |
auto offset=_pos; |
if(!getWord(word)) break; |
auto opcode=word>>26; |
|
std::string instruction; |
|
bool lcValid=false; |
Word lcOperand; |
|
switch(opcode) { |
case 0x10: |
instruction=decodeAdd(word); |
break; |
case 0x18: |
instruction=decodeAnd(word); |
break; |
case 0x21: |
instruction=decodeCall(word); |
break; |
case 0x15: |
instruction=decodeDivs(word); |
break; |
case 0x14: |
instruction=decodeDivu(word); |
break; |
case 0x02: |
instruction=decodeHlt(word); |
break; |
case 0x20: |
instruction=decodeJmp(word); |
break; |
case 0x01: |
instruction=decodeLc(word,lcValid,lcOperand); |
break; |
case 0x0B: |
instruction=decodeLsb(word); |
break; |
case 0x0A: |
instruction=decodeLub(word); |
break; |
case 0x08: |
instruction=decodeLw(word); |
break; |
case 0x17: |
instruction=decodeMods(word); |
break; |
case 0x16: |
instruction=decodeModu(word); |
break; |
case 0x12: |
instruction=decodeMul(word); |
break; |
case 0x00: |
instruction=decodeNop(word); |
break; |
case 0x19: |
instruction=decodeOr(word); |
break; |
case 0x0E: |
instruction=decodeSb(word); |
break; |
case 0x1C: |
instruction=decodeSl(word); |
break; |
case 0x1F: |
instruction=decodeSrs(word); |
break; |
case 0x1E: |
instruction=decodeSru(word); |
break; |
case 0x11: |
instruction=decodeSub(word); |
break; |
case 0x0C: |
instruction=decodeSw(word); |
break; |
case 0x1A: |
instruction=decodeXor(word); |
break; |
default: |
if((opcode>>4)==0x03) instruction=decodeCjmpxx(word); |
else if((opcode>>3)==0x05) instruction=decodeLcs(word); |
else instruction=decodeWord(word); |
} |
|
auto size=instruction.size(); |
std::size_t padding=0; |
if(size<32) padding=32-size; |
|
_os<<'\t'<<instruction<<std::string(padding,' ')<<"// "; |
_os<<hex(offset)<<": "<<hex(word); |
if(lcValid) _os<<' '<<hex(lcOperand); |
_os<<std::endl; |
} |
} |
|
bool Disassembler::getWord(Word &w) { |
if(_fmt==Bin) { |
char buf[sizeof(Word)] {}; // zero-initialize |
_is.read(buf,sizeof(Word)); |
|
auto n=static_cast<std::size_t>(_is.gcount()); |
if(n==0) return false; |
if(n<sizeof(Word)) std::cerr<<"Warning: last word is truncated"<<std::endl; |
|
w=(static_cast<unsigned char>(buf[3])<<24)|(static_cast<unsigned char>(buf[2])<<16)| |
(static_cast<unsigned char>(buf[1])<<8)|static_cast<unsigned char>(buf[0]); |
} |
else { |
try { |
std::string line; |
if(!std::getline(_is,line)) return false; |
_lineNumber++; |
|
if(_fmt==Textio) w=std::stoul(line,nullptr,2); |
else if(_fmt==Dec) w=std::stoul(line,nullptr,10); |
else if(_fmt==Hex) w=std::stoul(line,nullptr,16); |
else return false; |
} |
catch(std::exception &) { |
throw std::runtime_error("Bad literal at line "+std::to_string(_lineNumber)); |
} |
} |
_pos+=sizeof(Word); |
return true; |
} |
|
std::string Disassembler::str(const Operand &op) { |
if(op.type()==Operand::Register) { |
if(!_preferAliases) return "r"+std::to_string(op.value()); |
else if(op.value()>=240&&op.value()<=247) return "iv"+std::to_string(op.value()-240); |
else if(op.value()==252) return "cr"; |
else if(op.value()==253) return "irp"; |
else if(op.value()==254) return "rp"; |
else if(op.value()==255) return "sp"; |
else return "r"+std::to_string(op.value()); |
} |
else return std::to_string(op.value()); |
} |
|
Disassembler::Operand Disassembler::decodeRd1Operand(Word w) { |
int value=(w>>8)&0xFF; |
if(w&0x02000000) return Operand(Operand::Register,value); |
else { |
if(value>127) value-=256; |
return Operand(Operand::Direct,value); |
} |
} |
|
Disassembler::Operand Disassembler::decodeRd2Operand(Word w) { |
int value=w&0xFF; |
if(w&0x01000000) return Operand(Operand::Register,value); |
else { |
if(value>127) value-=256; |
return Operand(Operand::Direct,value); |
} |
} |
|
Disassembler::Operand Disassembler::decodeDstOperand(Word w) { |
int value=(w>>16)&0xFF; |
return Operand(Operand::Register,value); |
} |
|
std::string Disassembler::decodeSimpleInstruction(const std::string &op,Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
return op+' '+str(dst)+", "+str(rd1)+", "+str(rd2); |
} |
|
std::string Disassembler::decodeAdd(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd2.type()==Operand::Direct&&rd2.value()==0&&_preferAliases) |
return "mov "+str(dst)+", "+str(rd1); |
else |
return "add "+str(dst)+", "+str(rd1)+", "+str(rd2); |
} |
|
std::string Disassembler::decodeAnd(Word w) { |
return decodeSimpleInstruction("and",w); |
} |
|
std::string Disassembler::decodeCall(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(dst.value()!=0xFE) return decodeWord(w); |
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
return "call "+str(rd1); |
} |
|
std::string Disassembler::decodeCjmpxx(Word w) { |
auto jumpType=(w>>26)&0x0F; |
std::string op; |
|
switch(jumpType) { |
case 0x8: |
op="cjmpe"; |
break; |
case 0x4: |
op="cjmpne"; |
break; |
case 0x2: |
op="cjmpug"; |
break; |
case 0xA: |
op="cjmpuge"; |
break; |
case 0x1: |
op="cjmpsg"; |
break; |
case 0x9: |
op="cjmpsge"; |
break; |
default: |
return decodeWord(w); |
} |
|
return decodeSimpleInstruction(op,w); |
} |
|
std::string Disassembler::decodeDivs(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&rd2.value()==0) return decodeWord(w); |
return decodeSimpleInstruction("divs",w); |
} |
|
std::string Disassembler::decodeDivu(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&rd2.value()==0) return decodeWord(w); |
return decodeSimpleInstruction("divu",w); |
} |
|
std::string Disassembler::decodeHlt(Word w) { |
if(w!=0x08000000) return decodeWord(w); |
return "hlt"; |
} |
|
std::string Disassembler::decodeJmp(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(dst.value()!=0) return decodeWord(w); |
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
if(rd1.value()==253&&_preferAliases) return "iret"; |
if(rd1.value()==254&&_preferAliases) return "ret"; |
return "jmp "+str(rd1); |
} |
|
std::string Disassembler::decodeLc(Word w,bool &valid,Word &operand) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
valid=false; |
|
if(rd1.type()!=Operand::Direct||rd1.value()!=0) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
bool b=getWord(operand); |
if(!b) return decodeWord(w); |
|
valid=true; |
return "lc "+str(dst)+", 0x"+hex(operand); |
} |
|
std::string Disassembler::decodeLcs(Word w) { |
auto dst=decodeDstOperand(w); |
auto operand=w&0xFFFF; |
operand|=(w>>8)&0x001F0000; |
if(operand&0x00100000) operand|=0xFFE00000; |
return "lcs "+str(dst)+", 0x"+hex(operand); |
} |
|
std::string Disassembler::decodeLsb(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
return "lsb "+str(dst)+", "+str(rd1); |
} |
|
std::string Disassembler::decodeLub(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
return "lub "+str(dst)+", "+str(rd1); |
} |
|
std::string Disassembler::decodeLw(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
return "lw "+str(dst)+", "+str(rd1); |
} |
|
std::string Disassembler::decodeMods(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&rd2.value()==0) return decodeWord(w); |
return decodeSimpleInstruction("mods",w); |
} |
|
std::string Disassembler::decodeModu(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&rd2.value()==0) return decodeWord(w); |
return decodeSimpleInstruction("modu",w); |
} |
|
std::string Disassembler::decodeMul(Word w) { |
return decodeSimpleInstruction("mul",w); |
} |
|
std::string Disassembler::decodeNop(Word w) { |
if(w!=0) return decodeWord(w); |
return "nop"; |
} |
|
std::string Disassembler::decodeOr(Word w) { |
return decodeSimpleInstruction("or",w); |
} |
|
std::string Disassembler::decodeSb(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(dst.value()!=0) return decodeWord(w); |
if(rd1.type()!=Operand::Register) return decodeWord(w); |
|
return "sb "+str(rd1)+", "+str(rd2); |
} |
|
std::string Disassembler::decodeSl(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&(rd2.value()<0||rd2.value()>31)) return decodeWord(w); |
return decodeSimpleInstruction("sl",w); |
} |
|
std::string Disassembler::decodeSrs(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&(rd2.value()<0||rd2.value()>31)) return decodeWord(w); |
return decodeSimpleInstruction("srs",w); |
} |
|
std::string Disassembler::decodeSru(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&(rd2.value()<0||rd2.value()>31)) return decodeWord(w); |
return decodeSimpleInstruction("sru",w); |
} |
|
std::string Disassembler::decodeSub(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd1.type()==Operand::Direct&&rd1.value()==0&&_preferAliases) |
return "neg "+str(dst)+", "+str(rd2); |
else |
return "sub "+str(dst)+", "+str(rd1)+", "+str(rd2); |
} |
|
std::string Disassembler::decodeSw(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(dst.value()!=0) return decodeWord(w); |
if(rd1.type()!=Operand::Register) return decodeWord(w); |
|
return "sw "+str(rd1)+", "+str(rd2); |
} |
|
std::string Disassembler::decodeXor(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd2.type()==Operand::Direct&&rd2.value()==-1&&_preferAliases) |
return "not "+str(dst)+", "+str(rd1); |
else |
return "xor "+str(dst)+", "+str(rd1)+", "+str(rd2); |
} |
|
std::string Disassembler::decodeWord(Word w) { |
return ".word 0x"+hex(w); |
} |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Disassembler class. |
*/ |
|
#include "disassembler.h" |
|
#include <sstream> |
#include <stdexcept> |
|
/* |
* Disassembler::Operand class members |
*/ |
|
Disassembler::Operand::Operand(Type t,int value): |
_type(t),_value(value) {} |
|
Disassembler::Operand::Type Disassembler::Operand::type() const { |
return _type; |
} |
|
int Disassembler::Operand::value() const { |
return _value; |
} |
|
/* |
* Disassembler class members |
*/ |
|
Disassembler::Disassembler(std::istream &is,std::ostream &os): |
_is(is),_os(os),_fmt(Bin),_preferAliases(true),_lineNumber(0),_pos(0) {} |
|
void Disassembler::setFormat(Format fmt) { |
_fmt=fmt; |
} |
|
void Disassembler::setBase(Word base) { |
_pos=base; |
} |
|
void Disassembler::setPreferAliases(bool b) { |
_preferAliases=b; |
} |
|
void Disassembler::dump() { |
Word word; |
|
for(;;) { |
auto offset=_pos; |
if(!getWord(word)) break; |
auto opcode=word>>26; |
|
std::string instruction; |
|
bool lcValid=false; |
Word lcOperand; |
|
switch(opcode) { |
case 0x10: |
instruction=decodeAdd(word); |
break; |
case 0x18: |
instruction=decodeAnd(word); |
break; |
case 0x21: |
instruction=decodeCall(word); |
break; |
case 0x15: |
instruction=decodeDivs(word); |
break; |
case 0x14: |
instruction=decodeDivu(word); |
break; |
case 0x02: |
instruction=decodeHlt(word); |
break; |
case 0x20: |
instruction=decodeJmp(word); |
break; |
case 0x01: |
instruction=decodeLc(word,lcValid,lcOperand); |
break; |
case 0x0B: |
instruction=decodeLsb(word); |
break; |
case 0x0A: |
instruction=decodeLub(word); |
break; |
case 0x08: |
instruction=decodeLw(word); |
break; |
case 0x17: |
instruction=decodeMods(word); |
break; |
case 0x16: |
instruction=decodeModu(word); |
break; |
case 0x12: |
instruction=decodeMul(word); |
break; |
case 0x00: |
instruction=decodeNop(word); |
break; |
case 0x19: |
instruction=decodeOr(word); |
break; |
case 0x0E: |
instruction=decodeSb(word); |
break; |
case 0x1C: |
instruction=decodeSl(word); |
break; |
case 0x1F: |
instruction=decodeSrs(word); |
break; |
case 0x1E: |
instruction=decodeSru(word); |
break; |
case 0x11: |
instruction=decodeSub(word); |
break; |
case 0x0C: |
instruction=decodeSw(word); |
break; |
case 0x1A: |
instruction=decodeXor(word); |
break; |
default: |
if((opcode>>4)==0x03) instruction=decodeCjmpxx(word); |
else if((opcode>>3)==0x05) instruction=decodeLcs(word); |
else instruction=decodeWord(word); |
} |
|
auto size=instruction.size(); |
std::size_t padding=0; |
if(size<32) padding=32-size; |
|
_os<<'\t'<<instruction<<std::string(padding,' ')<<"// "; |
_os<<hex(offset)<<": "<<hex(word); |
if(lcValid) _os<<' '<<hex(lcOperand); |
_os<<std::endl; |
} |
} |
|
bool Disassembler::getWord(Word &w) { |
if(_fmt==Bin) { |
char buf[sizeof(Word)] {}; // zero-initialize |
_is.read(buf,sizeof(Word)); |
|
auto n=static_cast<std::size_t>(_is.gcount()); |
if(n==0) return false; |
if(n<sizeof(Word)) std::cerr<<"Warning: last word is truncated"<<std::endl; |
|
w=(static_cast<unsigned char>(buf[3])<<24)|(static_cast<unsigned char>(buf[2])<<16)| |
(static_cast<unsigned char>(buf[1])<<8)|static_cast<unsigned char>(buf[0]); |
} |
else { |
try { |
std::string line; |
if(!std::getline(_is,line)) return false; |
_lineNumber++; |
|
if(_fmt==Textio) w=std::stoul(line,nullptr,2); |
else if(_fmt==Dec) w=std::stoul(line,nullptr,10); |
else if(_fmt==Hex) w=std::stoul(line,nullptr,16); |
else return false; |
} |
catch(std::exception &) { |
throw std::runtime_error("Bad literal at line "+std::to_string(_lineNumber)); |
} |
} |
_pos+=sizeof(Word); |
return true; |
} |
|
std::string Disassembler::str(const Operand &op) { |
if(op.type()==Operand::Register) { |
if(!_preferAliases) return "r"+std::to_string(op.value()); |
else if(op.value()>=240&&op.value()<=247) return "iv"+std::to_string(op.value()-240); |
else if(op.value()==252) return "cr"; |
else if(op.value()==253) return "irp"; |
else if(op.value()==254) return "rp"; |
else if(op.value()==255) return "sp"; |
else return "r"+std::to_string(op.value()); |
} |
else return std::to_string(op.value()); |
} |
|
Disassembler::Operand Disassembler::decodeRd1Operand(Word w) { |
int value=(w>>8)&0xFF; |
if(w&0x02000000) return Operand(Operand::Register,value); |
else { |
if(value>127) value-=256; |
return Operand(Operand::Direct,value); |
} |
} |
|
Disassembler::Operand Disassembler::decodeRd2Operand(Word w) { |
int value=w&0xFF; |
if(w&0x01000000) return Operand(Operand::Register,value); |
else { |
if(value>127) value-=256; |
return Operand(Operand::Direct,value); |
} |
} |
|
Disassembler::Operand Disassembler::decodeDstOperand(Word w) { |
int value=(w>>16)&0xFF; |
return Operand(Operand::Register,value); |
} |
|
std::string Disassembler::decodeSimpleInstruction(const std::string &op,Word w) { |
std::ostringstream oss; |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
oss<<op<<' '<<str(dst)<<", "<<str(rd1)<<", "<<str(rd2); |
return oss.str(); |
} |
|
std::string Disassembler::decodeAdd(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd2.type()==Operand::Direct&&rd2.value()==0&&_preferAliases) |
oss<<"mov "<<str(dst)<<", "<<str(rd1); |
else |
oss<<"add "<<str(dst)<<", "<<str(rd1)<<", "<<str(rd2); |
|
return oss.str(); |
} |
|
std::string Disassembler::decodeAnd(Word w) { |
return decodeSimpleInstruction("and",w); |
} |
|
std::string Disassembler::decodeCall(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(dst.value()!=0xFE) return decodeWord(w); |
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
return "call "+str(rd1); |
} |
|
std::string Disassembler::decodeCjmpxx(Word w) { |
auto jumpType=(w>>26)&0x0F; |
std::string op; |
|
switch(jumpType) { |
case 0x8: |
op="cjmpe"; |
break; |
case 0x4: |
op="cjmpne"; |
break; |
case 0x2: |
op="cjmpug"; |
break; |
case 0xA: |
op="cjmpuge"; |
break; |
case 0x1: |
op="cjmpsg"; |
break; |
case 0x9: |
op="cjmpsge"; |
break; |
default: |
return decodeWord(w); |
} |
|
return decodeSimpleInstruction(op,w); |
} |
|
std::string Disassembler::decodeDivs(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&rd2.value()==0) return decodeWord(w); |
return decodeSimpleInstruction("divs",w); |
} |
|
std::string Disassembler::decodeDivu(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&rd2.value()==0) return decodeWord(w); |
return decodeSimpleInstruction("divu",w); |
} |
|
std::string Disassembler::decodeHlt(Word w) { |
if(w!=0x08000000) return decodeWord(w); |
return "hlt"; |
} |
|
std::string Disassembler::decodeJmp(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(dst.value()!=0) return decodeWord(w); |
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
if(rd1.value()==253&&_preferAliases) return "iret"; |
if(rd1.value()==254&&_preferAliases) return "ret"; |
return "jmp "+str(rd1); |
} |
|
std::string Disassembler::decodeLc(Word w,bool &valid,Word &operand) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
valid=false; |
|
if(rd1.type()!=Operand::Direct||rd1.value()!=0) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
bool b=getWord(operand); |
if(!b) return decodeWord(w); |
|
valid=true; |
return "lc "+str(dst)+", 0x"+hex(operand); |
} |
|
std::string Disassembler::decodeLcs(Word w) { |
auto dst=decodeDstOperand(w); |
auto operand=w&0xFFFF; |
operand|=(w>>8)&0x001F0000; |
if(operand&0x00100000) operand|=0xFFE00000; |
return "lcs "+str(dst)+", 0x"+hex(operand); |
} |
|
std::string Disassembler::decodeLsb(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
return "lsb "+str(dst)+", "+str(rd1); |
} |
|
std::string Disassembler::decodeLub(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
return "lub "+str(dst)+", "+str(rd1); |
} |
|
std::string Disassembler::decodeLw(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
return "lw "+str(dst)+", "+str(rd1); |
} |
|
std::string Disassembler::decodeMods(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&rd2.value()==0) return decodeWord(w); |
return decodeSimpleInstruction("mods",w); |
} |
|
std::string Disassembler::decodeModu(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&rd2.value()==0) return decodeWord(w); |
return decodeSimpleInstruction("modu",w); |
} |
|
std::string Disassembler::decodeMul(Word w) { |
return decodeSimpleInstruction("mul",w); |
} |
|
std::string Disassembler::decodeNop(Word w) { |
if(w!=0) return decodeWord(w); |
return "nop"; |
} |
|
std::string Disassembler::decodeOr(Word w) { |
return decodeSimpleInstruction("or",w); |
} |
|
std::string Disassembler::decodeSb(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(dst.value()!=0) return decodeWord(w); |
if(rd1.type()!=Operand::Register) return decodeWord(w); |
|
return "sb "+str(rd1)+", "+str(rd2); |
} |
|
std::string Disassembler::decodeSl(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&(rd2.value()<0||rd2.value()>31)) return decodeWord(w); |
return decodeSimpleInstruction("sl",w); |
} |
|
std::string Disassembler::decodeSrs(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&(rd2.value()<0||rd2.value()>31)) return decodeWord(w); |
return decodeSimpleInstruction("srs",w); |
} |
|
std::string Disassembler::decodeSru(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&(rd2.value()<0||rd2.value()>31)) return decodeWord(w); |
return decodeSimpleInstruction("sru",w); |
} |
|
std::string Disassembler::decodeSub(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd1.type()==Operand::Direct&&rd1.value()==0&&_preferAliases) |
oss<<"neg "<<str(dst)<<", "<<str(rd2); |
else |
oss<<"sub "<<str(dst)<<", "<<str(rd1)<<", "<<str(rd2); |
|
return oss.str(); |
} |
|
std::string Disassembler::decodeSw(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(dst.value()!=0) return decodeWord(w); |
if(rd1.type()!=Operand::Register) return decodeWord(w); |
|
return "sw "+str(rd1)+", "+str(rd2); |
} |
|
std::string Disassembler::decodeXor(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd2.type()==Operand::Direct&&rd2.value()==-1&&_preferAliases) |
oss<<"not "<<str(dst)<<", "<<str(rd1); |
else |
oss<<"xor "<<str(dst)<<", "<<str(rd1)<<", "<<str(rd2); |
|
return oss.str(); |
} |
|
std::string Disassembler::decodeWord(Word w) { |
return ".word 0x"+hex(w); |
} |
/lxp32dump/disassembler.h
1,95 → 1,95
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Disassembler class which disassembles |
* LXP32 executable code. |
*/ |
|
#ifndef DISASSEMBLER_H_INCLUDED |
#define DISASSEMBLER_H_INCLUDED |
|
#include <iostream> |
#include <type_traits> |
#include <cstdint> |
|
class Disassembler { |
public: |
enum Format {Bin,Textio,Dec,Hex}; |
typedef std::uint32_t Word; |
private: |
class Operand { |
public: |
enum Type {Register,Direct}; |
private: |
Type _type; |
int _value; |
public: |
Operand(Type t,int value); |
Type type() const; |
int value() const; |
}; |
|
std::istream &_is; |
std::ostream &_os; |
Format _fmt; |
bool _preferAliases; |
int _lineNumber; |
Word _pos; |
public: |
Disassembler(std::istream &is,std::ostream &os); |
void setFormat(Format fmt); |
void setBase(Word base); |
void setPreferAliases(bool b); |
void dump(); |
|
template <typename T> static std::string hex(const T &w) { |
static_assert(std::is_integral<T>::value,"Argument must be of integral type"); |
const char *hexstr="0123456789ABCDEF"; |
std::string res; |
|
res.reserve(sizeof(T)*2); |
|
for(int i=sizeof(T)*8-4;i>=0;i-=4) { |
res.push_back(hexstr[(w>>i)&0x0F]); |
} |
return res; |
} |
private: |
bool getWord(Word &w); |
std::string str(const Operand &op); |
static Operand decodeRd1Operand(Word w); |
static Operand decodeRd2Operand(Word w); |
static Operand decodeDstOperand(Word w); |
|
std::string decodeSimpleInstruction(const std::string &op,Word w); |
std::string decodeAdd(Word w); |
std::string decodeAnd(Word w); |
std::string decodeCall(Word w); |
std::string decodeCjmpxx(Word w); |
std::string decodeDivs(Word w); |
std::string decodeDivu(Word w); |
std::string decodeHlt(Word w); |
std::string decodeJmp(Word w); |
std::string decodeLc(Word w,bool &valid,Word &operand); |
std::string decodeLcs(Word w); |
std::string decodeLsb(Word w); |
std::string decodeLub(Word w); |
std::string decodeLw(Word w); |
std::string decodeMods(Word w); |
std::string decodeModu(Word w); |
std::string decodeMul(Word w); |
std::string decodeNop(Word w); |
std::string decodeOr(Word w); |
std::string decodeSb(Word w); |
std::string decodeSl(Word w); |
std::string decodeSrs(Word w); |
std::string decodeSru(Word w); |
std::string decodeSub(Word w); |
std::string decodeSw(Word w); |
std::string decodeXor(Word w); |
std::string decodeWord(Word w); |
}; |
|
#endif |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Disassembler class which disassembles |
* LXP32 executable code. |
*/ |
|
#ifndef DISASSEMBLER_H_INCLUDED |
#define DISASSEMBLER_H_INCLUDED |
|
#include <iostream> |
#include <type_traits> |
#include <cstdint> |
|
class Disassembler { |
public: |
enum Format {Bin,Textio,Dec,Hex}; |
typedef std::uint32_t Word; |
private: |
class Operand { |
public: |
enum Type {Register,Direct}; |
private: |
Type _type; |
int _value; |
public: |
Operand(Type t,int value); |
Type type() const; |
int value() const; |
}; |
|
std::istream &_is; |
std::ostream &_os; |
Format _fmt; |
bool _preferAliases; |
int _lineNumber; |
Word _pos; |
public: |
Disassembler(std::istream &is,std::ostream &os); |
void setFormat(Format fmt); |
void setBase(Word base); |
void setPreferAliases(bool b); |
void dump(); |
|
template <typename T> static std::string hex(const T &w) { |
static_assert(std::is_integral<T>::value,"Argument must be of integral type"); |
const char *hexstr="0123456789ABCDEF"; |
std::string res; |
|
res.reserve(sizeof(T)*2); |
|
for(int i=sizeof(T)*8-4;i>=0;i-=4) { |
res.push_back(hexstr[(w>>i)&0x0F]); |
} |
return res; |
} |
private: |
bool getWord(Word &w); |
std::string str(const Operand &op); |
static Operand decodeRd1Operand(Word w); |
static Operand decodeRd2Operand(Word w); |
static Operand decodeDstOperand(Word w); |
|
std::string decodeSimpleInstruction(const std::string &op,Word w); |
std::string decodeAdd(Word w); |
std::string decodeAnd(Word w); |
std::string decodeCall(Word w); |
std::string decodeCjmpxx(Word w); |
std::string decodeDivs(Word w); |
std::string decodeDivu(Word w); |
std::string decodeHlt(Word w); |
std::string decodeJmp(Word w); |
std::string decodeLc(Word w,bool &valid,Word &operand); |
std::string decodeLcs(Word w); |
std::string decodeLsb(Word w); |
std::string decodeLub(Word w); |
std::string decodeLw(Word w); |
std::string decodeMods(Word w); |
std::string decodeModu(Word w); |
std::string decodeMul(Word w); |
std::string decodeNop(Word w); |
std::string decodeOr(Word w); |
std::string decodeSb(Word w); |
std::string decodeSl(Word w); |
std::string decodeSrs(Word w); |
std::string decodeSru(Word w); |
std::string decodeSub(Word w); |
std::string decodeSw(Word w); |
std::string decodeXor(Word w); |
std::string decodeWord(Word w); |
}; |
|
#endif |
/lxp32dump/main.cpp
1,199 → 1,199
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* Main translation unit for the LXP32 disassembler. |
*/ |
|
#ifdef _MSC_VER |
#define _CRT_SECURE_NO_WARNINGS |
#endif |
|
#include "disassembler.h" |
|
#include <iostream> |
#include <fstream> |
#include <string> |
#include <stdexcept> |
#include <cstring> |
#include <cstdlib> |
#include <cstdio> |
#include <ctime> |
|
static void displayUsage(std::ostream &os,const char *program) { |
os<<std::endl; |
os<<"Usage:"<<std::endl; |
os<<" "<<program<<" [ option(s) | input file ]"<<std::endl<<std::endl; |
|
os<<"Options:"<<std::endl; |
os<<" -b <addr> Base address (for comments only)"<<std::endl; |
os<<" -f <fmt> Input format (bin, textio, dec, hex), default: autodetect"<<std::endl; |
os<<" -h, --help Display a short help message"<<std::endl; |
os<<" -na Do not use instruction and register aliases"<<std::endl; |
os<<" -o <file> Output file name, default: standard output"<<std::endl; |
os<<" -- Do not interpret subsequent arguments as options"<<std::endl; |
} |
|
static Disassembler::Format detectInputFormat(std::istream &in) { |
static const std::size_t Size=256; |
static const char *textio="01\r\n \t"; |
static const char *dec="0123456789\r\n \t"; |
static const char *hex="0123456789ABCDEFabcdef\r\n \t"; |
|
char buf[Size]; |
in.read(buf,Size); |
auto s=static_cast<std::size_t>(in.gcount()); |
in.clear(); |
in.seekg(0); |
|
Disassembler::Format fmt=Disassembler::Textio; |
|
for(std::size_t i=0;i<s;i++) { |
if(fmt==Disassembler::Textio&&!strchr(textio,buf[i])) fmt=Disassembler::Dec; |
if(fmt==Disassembler::Dec&&!strchr(dec,buf[i])) fmt=Disassembler::Hex; |
if(fmt==Disassembler::Hex&&!strchr(hex,buf[i])) { |
fmt=Disassembler::Bin; |
break; |
} |
} |
|
return fmt; |
} |
|
int main(int argc,char *argv[]) try { |
std::string inputFileName,outputFileName; |
|
std::cerr<<"LXP32 Platform Disassembler"<<std::endl; |
std::cerr<<"Copyright (c) 2016-2019 by Alex I. Kuznetsov"<<std::endl; |
|
Disassembler::Format fmt=Disassembler::Bin; |
bool noMoreOptions=false; |
bool formatSpecified=false; |
Disassembler::Word base=0; |
bool noAliases=false; |
|
if(argc<=1) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
|
for(int i=1;i<argc;i++) { |
if(argv[i][0]!='-'||noMoreOptions) { |
if(inputFileName.empty()) inputFileName=argv[i]; |
else throw std::runtime_error("Only one input file name can be specified"); |
} |
else if(!strcmp(argv[i],"--")) noMoreOptions=true; |
else if(!strcmp(argv[i],"-b")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
try { |
base=std::stoul(argv[i],nullptr,0); |
if(base%4!=0) throw std::exception(); |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid base address"); |
} |
} |
else if(!strcmp(argv[i],"-f")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
if(!strcmp(argv[i],"bin")) fmt=Disassembler::Bin; |
else if(!strcmp(argv[i],"textio")) fmt=Disassembler::Textio; |
else if(!strcmp(argv[i],"dec")) fmt=Disassembler::Dec; |
else if(!strcmp(argv[i],"hex")) fmt=Disassembler::Hex; |
else throw std::runtime_error("Unrecognized input format"); |
formatSpecified=true; |
} |
else if(!strcmp(argv[i],"-h")||!strcmp(argv[i],"--help")) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
else if(!strcmp(argv[i],"-na")) { |
noAliases=true; |
} |
else if(!strcmp(argv[i],"-o")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
outputFileName=argv[i]; |
} |
else throw std::runtime_error(std::string("Unrecognized option: \"")+argv[i]+"\""); |
} |
|
if(!formatSpecified) { // auto-detect input file format |
std::ifstream in(inputFileName,std::ios_base::in|std::ios_base::binary); |
fmt=detectInputFormat(in); |
} |
|
std::ifstream in; |
|
if(fmt==Disassembler::Bin) in.open(inputFileName,std::ios_base::in|std::ios_base::binary); |
else in.open(inputFileName,std::ios_base::in); |
if(!in) throw std::runtime_error("Cannot open \""+inputFileName+"\""); |
|
std::ofstream out; |
std::ostream *os=&std::cout; |
if(!outputFileName.empty()) { |
out.open(outputFileName,std::ios_base::out); |
if(!out) throw std::runtime_error("Cannot open \""+outputFileName+"\""); |
os=&out; |
} |
|
auto t=std::time(NULL); |
char szTime[256]; |
auto r=std::strftime(szTime,256,"%c",std::localtime(&t)); |
if(r==0) szTime[0]='\0'; |
|
*os<<"/*"<<std::endl; |
*os<<" * Input file: "<<inputFileName<<std::endl; |
*os<<" * Input format: "; |
|
switch(fmt) { |
case Disassembler::Bin: |
*os<<"bin"; |
break; |
case Disassembler::Textio: |
*os<<"textio"; |
break; |
case Disassembler::Dec: |
*os<<"dec"; |
break; |
case Disassembler::Hex: |
*os<<"hex"; |
break; |
default: |
break; |
} |
|
if(!formatSpecified) *os<<" (autodetected)"; |
*os<<std::endl; |
*os<<" * Base address: 0x"<<Disassembler::hex(base)<<std::endl; |
*os<<" * Disassembled by lxp32dump at "<<szTime<<std::endl; |
*os<<" */"<<std::endl<<std::endl; |
|
Disassembler disasm(in,*os); |
disasm.setFormat(fmt); |
disasm.setBase(base); |
disasm.setPreferAliases(!noAliases); |
|
try { |
disasm.dump(); |
} |
catch(std::exception &) { |
if(!outputFileName.empty()) { |
out.close(); |
std::remove(outputFileName.c_str()); |
} |
throw; |
} |
} |
catch(std::exception &ex) { |
std::cerr<<"Error: "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* Main translation unit for the LXP32 disassembler. |
*/ |
|
#ifdef _MSC_VER |
#define _CRT_SECURE_NO_WARNINGS |
#endif |
|
#include "disassembler.h" |
|
#include <iostream> |
#include <fstream> |
#include <string> |
#include <stdexcept> |
#include <cstring> |
#include <cstdlib> |
#include <cstdio> |
#include <ctime> |
|
static void displayUsage(std::ostream &os,const char *program) { |
os<<std::endl; |
os<<"Usage:"<<std::endl; |
os<<" "<<program<<" [ option(s) | input file ]"<<std::endl<<std::endl; |
|
os<<"Options:"<<std::endl; |
os<<" -b <addr> Base address (for comments only)"<<std::endl; |
os<<" -f <fmt> Input format (bin, textio, dec, hex), default: autodetect"<<std::endl; |
os<<" -h, --help Display a short help message"<<std::endl; |
os<<" -na Do not use instruction and register aliases"<<std::endl; |
os<<" -o <file> Output file name, default: standard output"<<std::endl; |
os<<" -- Do not interpret subsequent arguments as options"<<std::endl; |
} |
|
static Disassembler::Format detectInputFormat(std::istream &in) { |
static const std::size_t Size=256; |
static const char *textio="01\r\n \t"; |
static const char *dec="0123456789\r\n \t"; |
static const char *hex="0123456789ABCDEFabcdef\r\n \t"; |
|
char buf[Size]; |
in.read(buf,Size); |
auto s=static_cast<std::size_t>(in.gcount()); |
in.clear(); |
in.seekg(0); |
|
Disassembler::Format fmt=Disassembler::Textio; |
|
for(std::size_t i=0;i<s;i++) { |
if(fmt==Disassembler::Textio&&!strchr(textio,buf[i])) fmt=Disassembler::Dec; |
if(fmt==Disassembler::Dec&&!strchr(dec,buf[i])) fmt=Disassembler::Hex; |
if(fmt==Disassembler::Hex&&!strchr(hex,buf[i])) { |
fmt=Disassembler::Bin; |
break; |
} |
} |
|
return fmt; |
} |
|
int main(int argc,char *argv[]) try { |
std::string inputFileName,outputFileName; |
|
std::cerr<<"LXP32 Platform Disassembler"<<std::endl; |
std::cerr<<"Copyright (c) 2016-2019 by Alex I. Kuznetsov"<<std::endl; |
|
Disassembler::Format fmt=Disassembler::Bin; |
bool noMoreOptions=false; |
bool formatSpecified=false; |
Disassembler::Word base=0; |
bool noAliases=false; |
|
if(argc<=1) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
|
for(int i=1;i<argc;i++) { |
if(argv[i][0]!='-'||noMoreOptions) { |
if(inputFileName.empty()) inputFileName=argv[i]; |
else throw std::runtime_error("Only one input file name can be specified"); |
} |
else if(!strcmp(argv[i],"--")) noMoreOptions=true; |
else if(!strcmp(argv[i],"-b")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
try { |
base=std::stoul(argv[i],nullptr,0); |
if(base%4!=0) throw std::exception(); |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid base address"); |
} |
} |
else if(!strcmp(argv[i],"-f")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
if(!strcmp(argv[i],"bin")) fmt=Disassembler::Bin; |
else if(!strcmp(argv[i],"textio")) fmt=Disassembler::Textio; |
else if(!strcmp(argv[i],"dec")) fmt=Disassembler::Dec; |
else if(!strcmp(argv[i],"hex")) fmt=Disassembler::Hex; |
else throw std::runtime_error("Unrecognized input format"); |
formatSpecified=true; |
} |
else if(!strcmp(argv[i],"-h")||!strcmp(argv[i],"--help")) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
else if(!strcmp(argv[i],"-na")) { |
noAliases=true; |
} |
else if(!strcmp(argv[i],"-o")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
outputFileName=argv[i]; |
} |
else throw std::runtime_error(std::string("Unrecognized option: \"")+argv[i]+"\""); |
} |
|
if(!formatSpecified) { // auto-detect input file format |
std::ifstream in(inputFileName,std::ios_base::in|std::ios_base::binary); |
fmt=detectInputFormat(in); |
} |
|
std::ifstream in; |
|
if(fmt==Disassembler::Bin) in.open(inputFileName,std::ios_base::in|std::ios_base::binary); |
else in.open(inputFileName,std::ios_base::in); |
if(!in) throw std::runtime_error("Cannot open \""+inputFileName+"\""); |
|
std::ofstream out; |
std::ostream *os=&std::cout; |
if(!outputFileName.empty()) { |
out.open(outputFileName,std::ios_base::out); |
if(!out) throw std::runtime_error("Cannot open \""+outputFileName+"\""); |
os=&out; |
} |
|
auto t=std::time(NULL); |
char szTime[256]; |
auto r=std::strftime(szTime,256,"%c",std::localtime(&t)); |
if(r==0) szTime[0]='\0'; |
|
*os<<"/*"<<std::endl; |
*os<<" * Input file: "<<inputFileName<<std::endl; |
*os<<" * Input format: "; |
|
switch(fmt) { |
case Disassembler::Bin: |
*os<<"bin"; |
break; |
case Disassembler::Textio: |
*os<<"textio"; |
break; |
case Disassembler::Dec: |
*os<<"dec"; |
break; |
case Disassembler::Hex: |
*os<<"hex"; |
break; |
default: |
break; |
} |
|
if(!formatSpecified) *os<<" (autodetected)"; |
*os<<std::endl; |
*os<<" * Base address: 0x"<<Disassembler::hex(base)<<std::endl; |
*os<<" * Disassembled by lxp32dump at "<<szTime<<std::endl; |
*os<<" */"<<std::endl<<std::endl; |
|
Disassembler disasm(in,*os); |
disasm.setFormat(fmt); |
disasm.setBase(base); |
disasm.setPreferAliases(!noAliases); |
|
try { |
disasm.dump(); |
} |
catch(std::exception &) { |
if(!outputFileName.empty()) { |
out.close(); |
std::remove(outputFileName.c_str()); |
} |
throw; |
} |
} |
catch(std::exception &ex) { |
std::cerr<<"Error: "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
/lxp32dump/CMakeLists.txt
1,7 → 1,7
cmake_minimum_required(VERSION 3.3.0) |
|
add_executable(lxp32dump disassembler.cpp main.cpp) |
|
# Install |
|
install(TARGETS lxp32dump DESTINATION .) |
cmake_minimum_required(VERSION 3.3.0) |
|
add_executable(lxp32dump disassembler.cpp main.cpp) |
|
# Install |
|
install(TARGETS lxp32dump DESTINATION .) |
/wigen/range.h
1,33 → 1,33
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Range class which represents |
* VHDL array ranges. |
*/ |
|
#ifndef RANGE_H_INCLUDED |
#define RANGE_H_INCLUDED |
|
#include <string> |
|
class Range { |
int _high; |
int _low; |
bool _valid; |
public: |
Range(); |
Range(int h,int l); |
|
void assign(int h,int l); |
void clear(); |
|
bool valid() const; |
int high() const; |
int low() const; |
int length() const; |
std::string toString() const; |
}; |
|
#endif |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Range class which represents |
* VHDL array ranges. |
*/ |
|
#ifndef RANGE_H_INCLUDED |
#define RANGE_H_INCLUDED |
|
#include <string> |
|
class Range { |
int _high; |
int _low; |
bool _valid; |
public: |
Range(); |
Range(int h,int l); |
|
void assign(int h,int l); |
void clear(); |
|
bool valid() const; |
int high() const; |
int low() const; |
int length() const; |
std::string toString() const; |
}; |
|
#endif |
/wigen/main.cpp
1,128 → 1,128
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* Main translation unit for the WISHBONE interconnect generator. |
*/ |
|
#include "generator.h" |
|
#include <iostream> |
#include <string> |
#include <stdexcept> |
#include <cstring> |
#include <cstdlib> |
|
static void displayUsage(std::ostream &os,const char *program) { |
os<<std::endl; |
os<<"Usage:"<<std::endl; |
os<<" "<<program<<" [ option(s) ] <nm> <ns> <ma> <sa> <ps> [ <pg> ]"<<std::endl<<std::endl; |
os<<" <nm> Number of masters"<<std::endl; |
os<<" <ns> Number of slaves"<<std::endl; |
os<<" <ma> Master address width"<<std::endl; |
os<<" <sa> Slave address width"<<std::endl; |
os<<" <ps> Port size"<<std::endl; |
os<<" <pg> Port granularity, default: port size"<<std::endl; |
os<<std::endl; |
|
os<<"Options:"<<std::endl; |
os<<" -e <entity> Entity name, default: \"intercon\""<<std::endl; |
os<<" -h, --help Display a short help message"<<std::endl; |
os<<" -o <file> Output file name, default: \"<entity>.vhd\""<<std::endl; |
os<<" -p Generate pipelined arbiter"<<std::endl; |
os<<" -r Generate registered feedback signals"<<std::endl; |
os<<" -u Generate unsafe slave decoder"<<std::endl; |
} |
|
int main(int argc,char *argv[]) try { |
std::cout<<"WISHBONE interconnect generator"<<std::endl; |
std::cout<<"Copyright (c) 2016 by Alex I. Kuznetsov"<<std::endl; |
|
if(argc<=1) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
|
Generator gen; |
std::string outputFileName; |
int mainArg=0; |
|
for(int i=1;i<argc;i++) { |
if(argv[i][0]=='-') { |
if(!strcmp(argv[i],"-e")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
gen.setEntityName(argv[i]); |
} |
else if(!strcmp(argv[i],"-h")||!strcmp(argv[i],"--help")) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
else if(!strcmp(argv[i],"-o")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
outputFileName=argv[i]; |
} |
else if(!strcmp(argv[i],"-p")) { |
gen.setPipelinedArbiter(true); |
} |
else if(!strcmp(argv[i],"-r")) { |
gen.setRegisteredFeedback(true); |
} |
else if(!strcmp(argv[i],"-u")) { |
gen.setUnsafeDecoder(true); |
} |
else throw std::runtime_error(std::string("Unrecognized option: \"")+argv[i]+"\""); |
} |
else { |
if(mainArg>5) throw std::runtime_error("Too many arguments"); |
|
int value; |
|
try { |
value=std::stoi(argv[i],nullptr,0); |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid value"); |
} |
|
switch(mainArg) { |
case 0: |
gen.setMasters(value); |
break; |
case 1: |
gen.setSlaves(value); |
break; |
case 2: |
gen.setAddrWidth(value); |
break; |
case 3: |
gen.setSlaveAddrWidth(value); |
break; |
case 4: |
gen.setPortSize(value); |
break; |
case 5: |
gen.setPortGranularity(value); |
break; |
} |
mainArg++; |
} |
} |
|
if(mainArg<5) throw std::runtime_error("Too few arguments"); |
if(mainArg==5) gen.setPortGranularity(gen.portSize()); |
|
if(outputFileName.empty()) outputFileName=gen.entityName()+".vhd"; |
|
gen.generate(outputFileName); |
} |
catch(std::exception &ex) { |
std::cerr<<"Error: "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* Main translation unit for the WISHBONE interconnect generator. |
*/ |
|
#include "generator.h" |
|
#include <iostream> |
#include <string> |
#include <stdexcept> |
#include <cstring> |
#include <cstdlib> |
|
static void displayUsage(std::ostream &os,const char *program) { |
os<<std::endl; |
os<<"Usage:"<<std::endl; |
os<<" "<<program<<" [ option(s) ] <nm> <ns> <ma> <sa> <ps> [ <pg> ]"<<std::endl<<std::endl; |
os<<" <nm> Number of masters"<<std::endl; |
os<<" <ns> Number of slaves"<<std::endl; |
os<<" <ma> Master address width"<<std::endl; |
os<<" <sa> Slave address width"<<std::endl; |
os<<" <ps> Port size"<<std::endl; |
os<<" <pg> Port granularity, default: port size"<<std::endl; |
os<<std::endl; |
|
os<<"Options:"<<std::endl; |
os<<" -e <entity> Entity name, default: \"intercon\""<<std::endl; |
os<<" -h, --help Display a short help message"<<std::endl; |
os<<" -o <file> Output file name, default: \"<entity>.vhd\""<<std::endl; |
os<<" -p Generate pipelined arbiter"<<std::endl; |
os<<" -r Generate registered feedback signals"<<std::endl; |
os<<" -u Generate unsafe slave decoder"<<std::endl; |
} |
|
int main(int argc,char *argv[]) try { |
std::cout<<"WISHBONE interconnect generator"<<std::endl; |
std::cout<<"Copyright (c) 2016 by Alex I. Kuznetsov"<<std::endl; |
|
if(argc<=1) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
|
Generator gen; |
std::string outputFileName; |
int mainArg=0; |
|
for(int i=1;i<argc;i++) { |
if(argv[i][0]=='-') { |
if(!strcmp(argv[i],"-e")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
gen.setEntityName(argv[i]); |
} |
else if(!strcmp(argv[i],"-h")||!strcmp(argv[i],"--help")) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
else if(!strcmp(argv[i],"-o")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
outputFileName=argv[i]; |
} |
else if(!strcmp(argv[i],"-p")) { |
gen.setPipelinedArbiter(true); |
} |
else if(!strcmp(argv[i],"-r")) { |
gen.setRegisteredFeedback(true); |
} |
else if(!strcmp(argv[i],"-u")) { |
gen.setUnsafeDecoder(true); |
} |
else throw std::runtime_error(std::string("Unrecognized option: \"")+argv[i]+"\""); |
} |
else { |
if(mainArg>5) throw std::runtime_error("Too many arguments"); |
|
int value; |
|
try { |
value=std::stoi(argv[i],nullptr,0); |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid value"); |
} |
|
switch(mainArg) { |
case 0: |
gen.setMasters(value); |
break; |
case 1: |
gen.setSlaves(value); |
break; |
case 2: |
gen.setAddrWidth(value); |
break; |
case 3: |
gen.setSlaveAddrWidth(value); |
break; |
case 4: |
gen.setPortSize(value); |
break; |
case 5: |
gen.setPortGranularity(value); |
break; |
} |
mainArg++; |
} |
} |
|
if(mainArg<5) throw std::runtime_error("Too few arguments"); |
if(mainArg==5) gen.setPortGranularity(gen.portSize()); |
|
if(outputFileName.empty()) outputFileName=gen.entityName()+".vhd"; |
|
gen.generate(outputFileName); |
} |
catch(std::exception &ex) { |
std::cerr<<"Error: "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
/wigen/generator.cpp
1,625 → 1,625
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Generator class. |
*/ |
|
#ifdef _MSC_VER |
#define _CRT_SECURE_NO_WARNINGS |
#endif |
|
#include "generator.h" |
|
#include <iostream> |
#include <fstream> |
#include <sstream> |
#include <stdexcept> |
#include <cctype> |
#include <ctime> |
|
Generator::Generator(): |
_masters(1), |
_slaves(1), |
_addrWidth(32), |
_portSize(32), |
_portGranularity(32), |
_entityName("intercon"), |
_pipelinedArbiter(false), |
_registeredFeedback(false), |
_unsafeDecoder(false) {} |
|
void Generator::setMasters(int i) { |
if(i<1) throw std::runtime_error("Invalid number of masters"); |
_masters=i; |
} |
|
void Generator::setSlaves(int i) { |
if(i<1) throw std::runtime_error("Invalid number of slaves"); |
_slaves=i; |
} |
|
void Generator::setAddrWidth(int i) { |
if(i<1) throw std::runtime_error("Invalid address width"); |
_addrWidth=i; |
} |
|
void Generator::setSlaveAddrWidth(int i) { |
if(i<1) throw std::runtime_error("Invalid slave address width"); |
_slaveAddrWidth=i; |
} |
|
void Generator::setPortSize(int i) { |
if(i!=8&&i!=16&&i!=32&&i!=64) |
throw std::runtime_error("Invalid port size: 8, 16, 32 or 64 expected"); |
_portSize=i; |
} |
|
void Generator::setPortGranularity(int i) { |
if(i!=8&&i!=16&&i!=32&&i!=64) |
throw std::runtime_error("Invalid port granularity: 8, 16, 32 or 64 expected"); |
_portGranularity=i; |
} |
|
void Generator::setEntityName(const std::string &str) try { |
if(str.empty()) throw std::exception(); |
// First character must be a letter |
if(!std::isalpha(str[0])) throw std::exception(); |
// Subsequent characters can be letters, digits or underscores |
for(std::size_t i=1;i<str.size();i++) |
if(!std::isalnum(str[i])&&str[i]!='_') throw std::exception(); |
|
_entityName=str; |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid entity name"); |
} |
|
void Generator::setPipelinedArbiter(bool b) { |
_pipelinedArbiter=b; |
} |
|
void Generator::setRegisteredFeedback(bool b) { |
_registeredFeedback=b; |
} |
|
void Generator::setUnsafeDecoder(bool b) { |
_unsafeDecoder=b; |
} |
|
int Generator::masters() const { |
return _masters; |
} |
|
int Generator::slaves() const { |
return _slaves; |
} |
|
int Generator::addrWidth() const { |
return _addrWidth; |
} |
|
int Generator::slaveAddrWidth() const { |
return _slaveAddrWidth; |
} |
|
int Generator::portSize() const { |
return _portSize; |
} |
|
int Generator::portGranularity() const { |
return _portGranularity; |
} |
|
std::string Generator::entityName() const { |
return _entityName; |
} |
|
bool Generator::pipelinedArbiter() const { |
return _pipelinedArbiter; |
} |
|
bool Generator::registeredFeedback() const { |
return _registeredFeedback; |
} |
|
bool Generator::unsafeDecoder() const { |
return _unsafeDecoder; |
} |
|
void Generator::generate(const std::string &filename) { |
prepare(); |
|
std::ofstream out(filename,std::ios_base::out); |
if(!out) throw std::runtime_error("Cannot open \""+filename+"\""); |
|
writeBanner(out); |
writePreamble(out); |
writeEntity(out); |
writeArchitecture(out); |
} |
|
/* |
* Private members |
*/ |
|
void Generator::prepare() { |
_fallbackSlave=false; |
if(slaves()<(1<<(addrWidth()-slaveAddrWidth()))&&!unsafeDecoder()) |
_fallbackSlave=true; |
|
_mastersRange.assign(masters()-1,0); |
if(_fallbackSlave) _slavesRange.assign(slaves(),0); |
else _slavesRange.assign(slaves()-1,0); |
|
int q=portSize()/portGranularity(); |
|
switch(q) { |
case 1: |
_addrRange.assign(addrWidth()-1,0); |
break; |
case 2: |
if(addrWidth()<=1) throw std::runtime_error("Invalid master address width"); |
_addrRange.assign(addrWidth()-1,1); |
_selRange.assign(1,0); |
break; |
case 4: |
if(addrWidth()<=2) throw std::runtime_error("Invalid master address width"); |
_addrRange.assign(addrWidth()-1,2); |
_selRange.assign(3,0); |
break; |
case 8: |
if(addrWidth()<=3) throw std::runtime_error("Invalid master address width"); |
_addrRange.assign(addrWidth()-1,3); |
_selRange.assign(7,0); |
break; |
default: |
throw std::runtime_error("Invalid port size/granularity combination"); |
} |
|
if(slaveAddrWidth()>addrWidth()) throw std::runtime_error("Invalid slave address width"); |
if(slaves()>(1<<(addrWidth()-slaveAddrWidth()))) throw std::runtime_error("Invalid slave address width"); |
if(slaveAddrWidth()<=_addrRange.low()) throw std::runtime_error("Invalid slave address width"); |
|
_slaveAddrRange.assign(slaveAddrWidth()-1,_addrRange.low()); |
|
if(slaves()>1) { |
_slaveDecoderRange.assign(_addrRange.high(),slaveAddrWidth()); |
if(unsafeDecoder()) { |
int requiredSize=0; |
while((1<<requiredSize)<slaves()) requiredSize++; |
_slaveDecoderRange.assign(_slaveDecoderRange.low()+requiredSize-1, |
_slaveDecoderRange.low()); |
} |
} |
_dataRange.assign(portSize()-1,0); |
} |
|
void Generator::writeBanner(std::ostream &os) { |
auto t=std::time(NULL); |
char szTime[256]; |
auto r=std::strftime(szTime,256,"%c",std::localtime(&t)); |
if(r==0) szTime[0]='\0'; |
|
os<<"---------------------------------------------------------------------"<<std::endl; |
os<<"-- Simple WISHBONE interconnect"<<std::endl; |
os<<"--"<<std::endl; |
os<<"-- Generated by wigen at "<<szTime<<std::endl; |
os<<"--"<<std::endl; |
os<<"-- Configuration:"<<std::endl; |
os<<"-- Number of masters: "<<masters()<<std::endl; |
os<<"-- Number of slaves: "<<slaves()<<std::endl; |
os<<"-- Master address width: "<<addrWidth()<<std::endl; |
os<<"-- Slave address width: "<<slaveAddrWidth()<<std::endl; |
os<<"-- Port size: "<<portSize()<<std::endl; |
os<<"-- Port granularity: "<<portGranularity()<<std::endl; |
os<<"-- Entity name: "<<entityName()<<std::endl; |
os<<"-- Pipelined arbiter: "<<(pipelinedArbiter()?"yes":"no")<<std::endl; |
os<<"-- Registered feedback: "<<(registeredFeedback()?"yes":"no")<<std::endl; |
os<<"-- Unsafe slave decoder: "<<(unsafeDecoder()?"yes":"no")<<std::endl; |
os<<"--"<<std::endl; |
os<<"-- Command line:"<<std::endl; |
os<<"-- wigen -e "<<entityName(); |
if(pipelinedArbiter()) os<<" -p"; |
if(registeredFeedback()) os<<" -r"; |
if(unsafeDecoder()) os<<" -u"; |
os<<" "<<masters()<<" "<<slaves()<<" "<<addrWidth()<<" "<<slaveAddrWidth(); |
os<<" "<<portSize()<<" "<<portGranularity()<<std::endl; |
os<<"---------------------------------------------------------------------"<<std::endl; |
os<<std::endl; |
} |
|
void Generator::writePreamble(std::ostream &os) { |
os<<"library ieee;"<<std::endl; |
os<<"use ieee.std_logic_1164.all;"<<std::endl; |
os<<std::endl; |
} |
|
void Generator::writeEntity(std::ostream &os) { |
os<<"entity "<<entityName()<<" is"<<std::endl; |
os<<"\tport("<<std::endl; |
os<<"\t\tclk_i: in std_logic;"<<std::endl; |
os<<"\t\trst_i: in std_logic;"<<std::endl; |
os<<std::endl; |
|
for(int i=0;i<masters();i++) { |
os<<"\t\ts"<<i<<"_cyc_i: in std_logic;"<<std::endl; |
os<<"\t\ts"<<i<<"_stb_i: in std_logic;"<<std::endl; |
os<<"\t\ts"<<i<<"_we_i: in std_logic;"<<std::endl; |
if(_selRange.valid()) |
os<<"\t\ts"<<i<<"_sel_i: in std_logic_vector("<<_selRange.toString()<<");"<<std::endl; |
if(registeredFeedback()) { |
os<<"\t\ts"<<i<<"_cti_i: in std_logic_vector(2 downto 0);"<<std::endl; |
os<<"\t\ts"<<i<<"_bte_i: in std_logic_vector(1 downto 0);"<<std::endl; |
} |
os<<"\t\ts"<<i<<"_ack_o: out std_logic;"<<std::endl; |
os<<"\t\ts"<<i<<"_adr_i: in std_logic_vector("<<_addrRange.toString()<<");"<<std::endl; |
os<<"\t\ts"<<i<<"_dat_i: in std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<"\t\ts"<<i<<"_dat_o: out std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<std::endl; |
} |
|
for(int i=0;i<slaves();i++) { |
os<<"\t\tm"<<i<<"_cyc_o: out std_logic;"<<std::endl; |
os<<"\t\tm"<<i<<"_stb_o: out std_logic;"<<std::endl; |
os<<"\t\tm"<<i<<"_we_o: out std_logic;"<<std::endl; |
if(_selRange.valid()) |
os<<"\t\tm"<<i<<"_sel_o: out std_logic_vector("<<_selRange.toString()<<");"<<std::endl; |
if(registeredFeedback()) { |
os<<"\t\tm"<<i<<"_cti_o: out std_logic_vector(2 downto 0);"<<std::endl; |
os<<"\t\tm"<<i<<"_bte_o: out std_logic_vector(1 downto 0);"<<std::endl; |
} |
os<<"\t\tm"<<i<<"_ack_i: in std_logic;"<<std::endl; |
os<<"\t\tm"<<i<<"_adr_o: out std_logic_vector("<<_slaveAddrRange.toString()<<");"<<std::endl; |
os<<"\t\tm"<<i<<"_dat_o: out std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<"\t\tm"<<i<<"_dat_i: in std_logic_vector("<<_dataRange.toString()<<")"; |
if(i!=slaves()-1) os<<";"<<std::endl<<std::endl; |
else os<<std::endl; |
} |
|
os<<"\t);"<<std::endl; |
os<<"end entity;"<<std::endl; |
os<<std::endl; |
} |
|
void Generator::writeArchitecture(std::ostream &os) { |
os<<"architecture rtl of "<<entityName()<<" is"<<std::endl; |
os<<std::endl; |
|
if(masters()>1) { |
os<<"signal request: std_logic_vector("<< |
_mastersRange.toString()<<");"<<std::endl; |
os<<"signal grant_next: std_logic_vector("<< |
_mastersRange.toString()<<");"<<std::endl; |
os<<"signal grant: std_logic_vector("<< |
_mastersRange.toString()<<");"<<std::endl; |
|
if(!pipelinedArbiter()) { |
os<<"signal grant_reg: std_logic_vector("<< |
_mastersRange.toString()<<"):=(others=>\'0\');"<<std::endl; |
} |
|
os<<std::endl; |
} |
|
if(slaves()>1) { |
os<<"signal select_slave: std_logic_vector("<< |
_slavesRange.toString()<<");"<<std::endl; |
os<<std::endl; |
} |
|
os<<"signal cyc_mux: std_logic;"<<std::endl; |
os<<"signal stb_mux: std_logic;"<<std::endl; |
os<<"signal we_mux: std_logic;"<<std::endl; |
if(_selRange.valid()) |
os<<"signal sel_mux: std_logic_vector("<<_selRange.toString()<<");"<<std::endl; |
if(registeredFeedback()) { |
os<<"signal cti_mux: std_logic_vector(2 downto 0);"<<std::endl; |
os<<"signal bte_mux: std_logic_vector(1 downto 0);"<<std::endl; |
} |
os<<"signal adr_mux: std_logic_vector("<<_addrRange.toString()<<");"<<std::endl; |
os<<"signal wdata_mux: std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<std::endl; |
|
os<<"signal ack_mux: std_logic;"<<std::endl; |
os<<"signal rdata_mux: std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<std::endl; |
|
os<<"begin"<<std::endl; |
os<<std::endl; |
|
if(masters()>1) writeArbiter(os); |
writeMasterMux(os); |
writeMasterDemux(os); |
writeSlaveMux(os); |
writeSlaveDemux(os); |
|
os<<"end architecture;"<<std::endl; |
} |
|
void Generator::writeArbiter(std::ostream &os) { |
os<<"-- ARBITER"<<std::endl; |
os<<"-- Selects the active master. Masters with lower port numbers"<<std::endl; |
os<<"-- have higher priority. Ongoing cycles are not interrupted."<<std::endl; |
os<<std::endl; |
|
os<<"request<="; |
for(int i=_mastersRange.high();i>=_mastersRange.low();i--) { |
os<<"s"<<i<<"_cyc_i"; |
if(i>0) os<<'&'; |
} |
os<<';'<<std::endl<<std::endl; |
|
os<<"grant_next<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<'\t'; |
os<<decodedLiteral(i,masters()); |
os<<" when request("<<i<<")=\'1\' else"<<std::endl; |
} |
os<<"\t(others=>\'0\');"<<std::endl; |
os<<std::endl; |
|
if(!pipelinedArbiter()) { |
os<<"grant<=grant_reg when (request and grant_reg)/="; |
os<<binaryLiteral(0,masters()); |
os<<" else grant_next;"<<std::endl; |
os<<std::endl; |
|
os<<"process (clk_i) is"<<std::endl; |
os<<"begin"<<std::endl; |
os<<"\tif rising_edge(clk_i) then"<<std::endl; |
os<<"\t\tif rst_i=\'1\' then"<<std::endl; |
os<<"\t\t\tgrant_reg<=(others=>\'0\');"<<std::endl; |
os<<"\t\telse"<<std::endl; |
os<<"\t\t\tgrant_reg<=grant;"<<std::endl; |
os<<"\t\tend if;"<<std::endl; |
os<<"\tend if;"<<std::endl; |
os<<"end process;"<<std::endl; |
os<<std::endl; |
} |
else { |
os<<"process (clk_i) is"<<std::endl; |
os<<"begin"<<std::endl; |
os<<"\tif rising_edge(clk_i) then"<<std::endl; |
os<<"\t\tif rst_i=\'1\' then"<<std::endl; |
os<<"\t\t\tgrant<=(others=>\'0\');"<<std::endl; |
os<<"\t\telsif (request and grant)="<<binaryLiteral(0,masters())<<" then"<<std::endl; |
os<<"\t\t\tgrant<=grant_next;"<<std::endl; |
os<<"\t\tend if;"<<std::endl; |
os<<"\tend if;"<<std::endl; |
os<<"end process;"<<std::endl; |
os<<std::endl; |
} |
} |
|
void Generator::writeMasterMux(std::ostream &os) { |
os<<"-- MASTER->SLAVE MUX"<<std::endl; |
os<<std::endl; |
|
if(masters()>1) { |
os<<"cyc_mux<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<'\t'; |
os<<"(s"<<i<<"_cyc_i and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<std::endl; |
|
os<<"stb_mux<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<'\t'; |
os<<"(s"<<i<<"_stb_i and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<std::endl; |
|
os<<"we_mux<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<'\t'; |
os<<"(s"<<i<<"_we_i and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<std::endl; |
|
if(_selRange.valid()) { |
os<<"sel_mux_gen: for i in sel_mux'range generate"<<std::endl; |
os<<"\tsel_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_sel_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
} |
|
if(registeredFeedback()) { |
os<<"cti_mux_gen: for i in cti_mux'range generate"<<std::endl; |
os<<"\tcti_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_cti_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
|
os<<"bte_mux_gen: for i in bte_mux'range generate"<<std::endl; |
os<<"\tbte_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_bte_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
} |
|
os<<"adr_mux_gen: for i in adr_mux'range generate"<<std::endl; |
os<<"\tadr_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_adr_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
|
os<<"wdata_mux_gen: for i in wdata_mux'range generate"<<std::endl; |
os<<"\twdata_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_dat_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
} |
else { // just one master |
os<<"cyc_mux<=s0_cyc_i;"<<std::endl; |
os<<"stb_mux<=s0_stb_i;"<<std::endl; |
os<<"we_mux<=s0_we_i;"<<std::endl; |
if(_selRange.valid()) os<<"sel_mux<=s0_sel_i;"<<std::endl; |
if(registeredFeedback()) { |
os<<"cti_mux<=s0_cti_i;"<<std::endl; |
os<<"bte_mux<=s0_bte_i;"<<std::endl; |
} |
os<<"adr_mux<=s0_adr_i;"<<std::endl; |
os<<"wdata_mux<=s0_dat_i;"<<std::endl; |
os<<std::endl; |
} |
} |
|
void Generator::writeMasterDemux(std::ostream &os) { |
os<<"-- MASTER->SLAVE DEMUX"<<std::endl; |
os<<std::endl; |
|
if(slaves()>1) { |
os<<"select_slave<="; |
for(int i=0;i<slaves();i++) { |
if(i>0) os<<'\t'; |
os<<decodedLiteral(i,_slavesRange.length()); |
os<<" when adr_mux("<<_slaveDecoderRange.toString()<<")="; |
os<<binaryLiteral(i,_slaveDecoderRange.length())<<" else"<<std::endl; |
} |
if(_fallbackSlave) { |
os<<'\t'<<decodedLiteral(slaves(),_slavesRange.length())<< |
"; -- fallback slave"<<std::endl; |
} |
else os<<"\t(others=>'-');"<<std::endl; |
os<<std::endl; |
|
for(int i=0;i<slaves();i++) { |
os<<'m'<<i<<"_cyc_o<=cyc_mux and select_slave("<<i<<");"<<std::endl; |
os<<'m'<<i<<"_stb_o<=stb_mux and select_slave("<<i<<");"<<std::endl; |
os<<'m'<<i<<"_we_o<=we_mux;"<<std::endl; |
if(_selRange.valid()) os<<'m'<<i<<"_sel_o<=sel_mux;"<<std::endl; |
if(registeredFeedback()) { |
os<<'m'<<i<<"_cti_o<=cti_mux;"<<std::endl; |
os<<'m'<<i<<"_bte_o<=bte_mux;"<<std::endl; |
} |
os<<'m'<<i<<"_adr_o<=adr_mux(m"<<i<<"_adr_o'range);"<<std::endl; |
os<<'m'<<i<<"_dat_o<=wdata_mux;"<<std::endl; |
os<<std::endl; |
} |
} |
else { // just one slave |
os<<"m0_cyc_o<=cyc_mux;"<<std::endl; |
os<<"m0_stb_o<=stb_mux;"<<std::endl; |
os<<"m0_we_o<=we_mux;"<<std::endl; |
if(_selRange.valid()) os<<"m0_sel_o<=sel_mux;"<<std::endl; |
if(registeredFeedback()) { |
os<<"m0_cti_o<=cti_mux;"<<std::endl; |
os<<"m0_bte_o<=bte_mux;"<<std::endl; |
} |
os<<"m0_adr_o<=adr_mux(m0_adr_o'range);"<<std::endl; |
os<<"m0_dat_o<=wdata_mux;"<<std::endl; |
os<<std::endl; |
} |
} |
|
void Generator::writeSlaveMux(std::ostream &os) { |
os<<"-- SLAVE->MASTER MUX"<<std::endl; |
os<<std::endl; |
|
if(slaves()>1) { |
os<<"ack_mux<="; |
for(int i=0;i<slaves();i++) { |
if(i>0) os<<'\t'; |
os<<"(m"<<i<<"_ack_i and select_slave("<<i<<"))"; |
if(i<slaves()-1||_fallbackSlave) os<<" or"<<std::endl; |
else os<<';'<<std::endl; |
} |
if(_fallbackSlave) { |
os<<"\t(cyc_mux and stb_mux and select_slave("<<slaves()<< |
")); -- fallback slave"<<std::endl; |
} |
os<<std::endl; |
|
os<<"rdata_mux_gen: for i in rdata_mux'range generate"<<std::endl; |
os<<"\trdata_mux(i)<="; |
for(int i=0;i<slaves();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(m"<<i<<"_dat_i(i) and select_slave("<<i<<"))"; |
if(i<slaves()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
} |
else { // just one slave |
os<<"ack_mux<=m0_ack_i;"<<std::endl; |
os<<"rdata_mux<=m0_dat_i;"<<std::endl; |
os<<std::endl; |
} |
} |
|
void Generator::writeSlaveDemux(std::ostream &os) { |
os<<"-- SLAVE->MASTER DEMUX"<<std::endl; |
os<<std::endl; |
|
if(masters()>1) { |
for(int i=0;i<masters();i++) { |
os<<'s'<<i<<"_ack_o<=ack_mux and grant("<<i<<");"<<std::endl; |
os<<'s'<<i<<"_dat_o<=rdata_mux;"<<std::endl; |
os<<std::endl; |
} |
} |
else { // just one master |
os<<"s0_ack_o<=ack_mux;"<<std::endl; |
os<<"s0_dat_o<=rdata_mux;"<<std::endl; |
os<<std::endl; |
} |
} |
|
std::string Generator::binaryLiteral(int value,int n) { |
std::ostringstream oss; |
oss.put('\"'); |
for(int i=n-1;i>=0;i--) { |
if(value>=static_cast<int>(sizeof(int)*8)) oss.put('0'); |
else if((value>>i)&1) oss.put('1'); |
else oss.put('0'); |
} |
oss.put('\"'); |
return oss.str(); |
} |
|
std::string Generator::decodedLiteral(int value,int n) { |
std::ostringstream oss; |
oss.put('\"'); |
for(int i=n-1;i>=0;i--) { |
if(value==i) oss.put('1'); |
else oss.put('0'); |
} |
oss.put('\"'); |
return oss.str(); |
} |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Generator class. |
*/ |
|
#ifdef _MSC_VER |
#define _CRT_SECURE_NO_WARNINGS |
#endif |
|
#include "generator.h" |
|
#include <iostream> |
#include <fstream> |
#include <sstream> |
#include <stdexcept> |
#include <cctype> |
#include <ctime> |
|
Generator::Generator(): |
_masters(1), |
_slaves(1), |
_addrWidth(32), |
_portSize(32), |
_portGranularity(32), |
_entityName("intercon"), |
_pipelinedArbiter(false), |
_registeredFeedback(false), |
_unsafeDecoder(false) {} |
|
void Generator::setMasters(int i) { |
if(i<1) throw std::runtime_error("Invalid number of masters"); |
_masters=i; |
} |
|
void Generator::setSlaves(int i) { |
if(i<1) throw std::runtime_error("Invalid number of slaves"); |
_slaves=i; |
} |
|
void Generator::setAddrWidth(int i) { |
if(i<1) throw std::runtime_error("Invalid address width"); |
_addrWidth=i; |
} |
|
void Generator::setSlaveAddrWidth(int i) { |
if(i<1) throw std::runtime_error("Invalid slave address width"); |
_slaveAddrWidth=i; |
} |
|
void Generator::setPortSize(int i) { |
if(i!=8&&i!=16&&i!=32&&i!=64) |
throw std::runtime_error("Invalid port size: 8, 16, 32 or 64 expected"); |
_portSize=i; |
} |
|
void Generator::setPortGranularity(int i) { |
if(i!=8&&i!=16&&i!=32&&i!=64) |
throw std::runtime_error("Invalid port granularity: 8, 16, 32 or 64 expected"); |
_portGranularity=i; |
} |
|
void Generator::setEntityName(const std::string &str) try { |
if(str.empty()) throw std::exception(); |
// First character must be a letter |
if(!std::isalpha(str[0])) throw std::exception(); |
// Subsequent characters can be letters, digits or underscores |
for(std::size_t i=1;i<str.size();i++) |
if(!std::isalnum(str[i])&&str[i]!='_') throw std::exception(); |
|
_entityName=str; |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid entity name"); |
} |
|
void Generator::setPipelinedArbiter(bool b) { |
_pipelinedArbiter=b; |
} |
|
void Generator::setRegisteredFeedback(bool b) { |
_registeredFeedback=b; |
} |
|
void Generator::setUnsafeDecoder(bool b) { |
_unsafeDecoder=b; |
} |
|
int Generator::masters() const { |
return _masters; |
} |
|
int Generator::slaves() const { |
return _slaves; |
} |
|
int Generator::addrWidth() const { |
return _addrWidth; |
} |
|
int Generator::slaveAddrWidth() const { |
return _slaveAddrWidth; |
} |
|
int Generator::portSize() const { |
return _portSize; |
} |
|
int Generator::portGranularity() const { |
return _portGranularity; |
} |
|
std::string Generator::entityName() const { |
return _entityName; |
} |
|
bool Generator::pipelinedArbiter() const { |
return _pipelinedArbiter; |
} |
|
bool Generator::registeredFeedback() const { |
return _registeredFeedback; |
} |
|
bool Generator::unsafeDecoder() const { |
return _unsafeDecoder; |
} |
|
void Generator::generate(const std::string &filename) { |
prepare(); |
|
std::ofstream out(filename,std::ios_base::out); |
if(!out) throw std::runtime_error("Cannot open \""+filename+"\""); |
|
writeBanner(out); |
writePreamble(out); |
writeEntity(out); |
writeArchitecture(out); |
} |
|
/* |
* Private members |
*/ |
|
void Generator::prepare() { |
_fallbackSlave=false; |
if(slaves()<(1<<(addrWidth()-slaveAddrWidth()))&&!unsafeDecoder()) |
_fallbackSlave=true; |
|
_mastersRange.assign(masters()-1,0); |
if(_fallbackSlave) _slavesRange.assign(slaves(),0); |
else _slavesRange.assign(slaves()-1,0); |
|
int q=portSize()/portGranularity(); |
|
switch(q) { |
case 1: |
_addrRange.assign(addrWidth()-1,0); |
break; |
case 2: |
if(addrWidth()<=1) throw std::runtime_error("Invalid master address width"); |
_addrRange.assign(addrWidth()-1,1); |
_selRange.assign(1,0); |
break; |
case 4: |
if(addrWidth()<=2) throw std::runtime_error("Invalid master address width"); |
_addrRange.assign(addrWidth()-1,2); |
_selRange.assign(3,0); |
break; |
case 8: |
if(addrWidth()<=3) throw std::runtime_error("Invalid master address width"); |
_addrRange.assign(addrWidth()-1,3); |
_selRange.assign(7,0); |
break; |
default: |
throw std::runtime_error("Invalid port size/granularity combination"); |
} |
|
if(slaveAddrWidth()>addrWidth()) throw std::runtime_error("Invalid slave address width"); |
if(slaves()>(1<<(addrWidth()-slaveAddrWidth()))) throw std::runtime_error("Invalid slave address width"); |
if(slaveAddrWidth()<=_addrRange.low()) throw std::runtime_error("Invalid slave address width"); |
|
_slaveAddrRange.assign(slaveAddrWidth()-1,_addrRange.low()); |
|
if(slaves()>1) { |
_slaveDecoderRange.assign(_addrRange.high(),slaveAddrWidth()); |
if(unsafeDecoder()) { |
int requiredSize=0; |
while((1<<requiredSize)<slaves()) requiredSize++; |
_slaveDecoderRange.assign(_slaveDecoderRange.low()+requiredSize-1, |
_slaveDecoderRange.low()); |
} |
} |
_dataRange.assign(portSize()-1,0); |
} |
|
void Generator::writeBanner(std::ostream &os) { |
auto t=std::time(NULL); |
char szTime[256]; |
auto r=std::strftime(szTime,256,"%c",std::localtime(&t)); |
if(r==0) szTime[0]='\0'; |
|
os<<"---------------------------------------------------------------------"<<std::endl; |
os<<"-- Simple WISHBONE interconnect"<<std::endl; |
os<<"--"<<std::endl; |
os<<"-- Generated by wigen at "<<szTime<<std::endl; |
os<<"--"<<std::endl; |
os<<"-- Configuration:"<<std::endl; |
os<<"-- Number of masters: "<<masters()<<std::endl; |
os<<"-- Number of slaves: "<<slaves()<<std::endl; |
os<<"-- Master address width: "<<addrWidth()<<std::endl; |
os<<"-- Slave address width: "<<slaveAddrWidth()<<std::endl; |
os<<"-- Port size: "<<portSize()<<std::endl; |
os<<"-- Port granularity: "<<portGranularity()<<std::endl; |
os<<"-- Entity name: "<<entityName()<<std::endl; |
os<<"-- Pipelined arbiter: "<<(pipelinedArbiter()?"yes":"no")<<std::endl; |
os<<"-- Registered feedback: "<<(registeredFeedback()?"yes":"no")<<std::endl; |
os<<"-- Unsafe slave decoder: "<<(unsafeDecoder()?"yes":"no")<<std::endl; |
os<<"--"<<std::endl; |
os<<"-- Command line:"<<std::endl; |
os<<"-- wigen -e "<<entityName(); |
if(pipelinedArbiter()) os<<" -p"; |
if(registeredFeedback()) os<<" -r"; |
if(unsafeDecoder()) os<<" -u"; |
os<<" "<<masters()<<" "<<slaves()<<" "<<addrWidth()<<" "<<slaveAddrWidth(); |
os<<" "<<portSize()<<" "<<portGranularity()<<std::endl; |
os<<"---------------------------------------------------------------------"<<std::endl; |
os<<std::endl; |
} |
|
void Generator::writePreamble(std::ostream &os) { |
os<<"library ieee;"<<std::endl; |
os<<"use ieee.std_logic_1164.all;"<<std::endl; |
os<<std::endl; |
} |
|
void Generator::writeEntity(std::ostream &os) { |
os<<"entity "<<entityName()<<" is"<<std::endl; |
os<<"\tport("<<std::endl; |
os<<"\t\tclk_i: in std_logic;"<<std::endl; |
os<<"\t\trst_i: in std_logic;"<<std::endl; |
os<<std::endl; |
|
for(int i=0;i<masters();i++) { |
os<<"\t\ts"<<i<<"_cyc_i: in std_logic;"<<std::endl; |
os<<"\t\ts"<<i<<"_stb_i: in std_logic;"<<std::endl; |
os<<"\t\ts"<<i<<"_we_i: in std_logic;"<<std::endl; |
if(_selRange.valid()) |
os<<"\t\ts"<<i<<"_sel_i: in std_logic_vector("<<_selRange.toString()<<");"<<std::endl; |
if(registeredFeedback()) { |
os<<"\t\ts"<<i<<"_cti_i: in std_logic_vector(2 downto 0);"<<std::endl; |
os<<"\t\ts"<<i<<"_bte_i: in std_logic_vector(1 downto 0);"<<std::endl; |
} |
os<<"\t\ts"<<i<<"_ack_o: out std_logic;"<<std::endl; |
os<<"\t\ts"<<i<<"_adr_i: in std_logic_vector("<<_addrRange.toString()<<");"<<std::endl; |
os<<"\t\ts"<<i<<"_dat_i: in std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<"\t\ts"<<i<<"_dat_o: out std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<std::endl; |
} |
|
for(int i=0;i<slaves();i++) { |
os<<"\t\tm"<<i<<"_cyc_o: out std_logic;"<<std::endl; |
os<<"\t\tm"<<i<<"_stb_o: out std_logic;"<<std::endl; |
os<<"\t\tm"<<i<<"_we_o: out std_logic;"<<std::endl; |
if(_selRange.valid()) |
os<<"\t\tm"<<i<<"_sel_o: out std_logic_vector("<<_selRange.toString()<<");"<<std::endl; |
if(registeredFeedback()) { |
os<<"\t\tm"<<i<<"_cti_o: out std_logic_vector(2 downto 0);"<<std::endl; |
os<<"\t\tm"<<i<<"_bte_o: out std_logic_vector(1 downto 0);"<<std::endl; |
} |
os<<"\t\tm"<<i<<"_ack_i: in std_logic;"<<std::endl; |
os<<"\t\tm"<<i<<"_adr_o: out std_logic_vector("<<_slaveAddrRange.toString()<<");"<<std::endl; |
os<<"\t\tm"<<i<<"_dat_o: out std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<"\t\tm"<<i<<"_dat_i: in std_logic_vector("<<_dataRange.toString()<<")"; |
if(i!=slaves()-1) os<<";"<<std::endl<<std::endl; |
else os<<std::endl; |
} |
|
os<<"\t);"<<std::endl; |
os<<"end entity;"<<std::endl; |
os<<std::endl; |
} |
|
void Generator::writeArchitecture(std::ostream &os) { |
os<<"architecture rtl of "<<entityName()<<" is"<<std::endl; |
os<<std::endl; |
|
if(masters()>1) { |
os<<"signal request: std_logic_vector("<< |
_mastersRange.toString()<<");"<<std::endl; |
os<<"signal grant_next: std_logic_vector("<< |
_mastersRange.toString()<<");"<<std::endl; |
os<<"signal grant: std_logic_vector("<< |
_mastersRange.toString()<<");"<<std::endl; |
|
if(!pipelinedArbiter()) { |
os<<"signal grant_reg: std_logic_vector("<< |
_mastersRange.toString()<<"):=(others=>\'0\');"<<std::endl; |
} |
|
os<<std::endl; |
} |
|
if(slaves()>1) { |
os<<"signal select_slave: std_logic_vector("<< |
_slavesRange.toString()<<");"<<std::endl; |
os<<std::endl; |
} |
|
os<<"signal cyc_mux: std_logic;"<<std::endl; |
os<<"signal stb_mux: std_logic;"<<std::endl; |
os<<"signal we_mux: std_logic;"<<std::endl; |
if(_selRange.valid()) |
os<<"signal sel_mux: std_logic_vector("<<_selRange.toString()<<");"<<std::endl; |
if(registeredFeedback()) { |
os<<"signal cti_mux: std_logic_vector(2 downto 0);"<<std::endl; |
os<<"signal bte_mux: std_logic_vector(1 downto 0);"<<std::endl; |
} |
os<<"signal adr_mux: std_logic_vector("<<_addrRange.toString()<<");"<<std::endl; |
os<<"signal wdata_mux: std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<std::endl; |
|
os<<"signal ack_mux: std_logic;"<<std::endl; |
os<<"signal rdata_mux: std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<std::endl; |
|
os<<"begin"<<std::endl; |
os<<std::endl; |
|
if(masters()>1) writeArbiter(os); |
writeMasterMux(os); |
writeMasterDemux(os); |
writeSlaveMux(os); |
writeSlaveDemux(os); |
|
os<<"end architecture;"<<std::endl; |
} |
|
void Generator::writeArbiter(std::ostream &os) { |
os<<"-- ARBITER"<<std::endl; |
os<<"-- Selects the active master. Masters with lower port numbers"<<std::endl; |
os<<"-- have higher priority. Ongoing cycles are not interrupted."<<std::endl; |
os<<std::endl; |
|
os<<"request<="; |
for(int i=_mastersRange.high();i>=_mastersRange.low();i--) { |
os<<"s"<<i<<"_cyc_i"; |
if(i>0) os<<'&'; |
} |
os<<';'<<std::endl<<std::endl; |
|
os<<"grant_next<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<'\t'; |
os<<decodedLiteral(i,masters()); |
os<<" when request("<<i<<")=\'1\' else"<<std::endl; |
} |
os<<"\t(others=>\'0\');"<<std::endl; |
os<<std::endl; |
|
if(!pipelinedArbiter()) { |
os<<"grant<=grant_reg when (request and grant_reg)/="; |
os<<binaryLiteral(0,masters()); |
os<<" else grant_next;"<<std::endl; |
os<<std::endl; |
|
os<<"process (clk_i) is"<<std::endl; |
os<<"begin"<<std::endl; |
os<<"\tif rising_edge(clk_i) then"<<std::endl; |
os<<"\t\tif rst_i=\'1\' then"<<std::endl; |
os<<"\t\t\tgrant_reg<=(others=>\'0\');"<<std::endl; |
os<<"\t\telse"<<std::endl; |
os<<"\t\t\tgrant_reg<=grant;"<<std::endl; |
os<<"\t\tend if;"<<std::endl; |
os<<"\tend if;"<<std::endl; |
os<<"end process;"<<std::endl; |
os<<std::endl; |
} |
else { |
os<<"process (clk_i) is"<<std::endl; |
os<<"begin"<<std::endl; |
os<<"\tif rising_edge(clk_i) then"<<std::endl; |
os<<"\t\tif rst_i=\'1\' then"<<std::endl; |
os<<"\t\t\tgrant<=(others=>\'0\');"<<std::endl; |
os<<"\t\telsif (request and grant)="<<binaryLiteral(0,masters())<<" then"<<std::endl; |
os<<"\t\t\tgrant<=grant_next;"<<std::endl; |
os<<"\t\tend if;"<<std::endl; |
os<<"\tend if;"<<std::endl; |
os<<"end process;"<<std::endl; |
os<<std::endl; |
} |
} |
|
void Generator::writeMasterMux(std::ostream &os) { |
os<<"-- MASTER->SLAVE MUX"<<std::endl; |
os<<std::endl; |
|
if(masters()>1) { |
os<<"cyc_mux<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<'\t'; |
os<<"(s"<<i<<"_cyc_i and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<std::endl; |
|
os<<"stb_mux<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<'\t'; |
os<<"(s"<<i<<"_stb_i and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<std::endl; |
|
os<<"we_mux<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<'\t'; |
os<<"(s"<<i<<"_we_i and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<std::endl; |
|
if(_selRange.valid()) { |
os<<"sel_mux_gen: for i in sel_mux'range generate"<<std::endl; |
os<<"\tsel_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_sel_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
} |
|
if(registeredFeedback()) { |
os<<"cti_mux_gen: for i in cti_mux'range generate"<<std::endl; |
os<<"\tcti_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_cti_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
|
os<<"bte_mux_gen: for i in bte_mux'range generate"<<std::endl; |
os<<"\tbte_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_bte_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
} |
|
os<<"adr_mux_gen: for i in adr_mux'range generate"<<std::endl; |
os<<"\tadr_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_adr_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
|
os<<"wdata_mux_gen: for i in wdata_mux'range generate"<<std::endl; |
os<<"\twdata_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_dat_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
} |
else { // just one master |
os<<"cyc_mux<=s0_cyc_i;"<<std::endl; |
os<<"stb_mux<=s0_stb_i;"<<std::endl; |
os<<"we_mux<=s0_we_i;"<<std::endl; |
if(_selRange.valid()) os<<"sel_mux<=s0_sel_i;"<<std::endl; |
if(registeredFeedback()) { |
os<<"cti_mux<=s0_cti_i;"<<std::endl; |
os<<"bte_mux<=s0_bte_i;"<<std::endl; |
} |
os<<"adr_mux<=s0_adr_i;"<<std::endl; |
os<<"wdata_mux<=s0_dat_i;"<<std::endl; |
os<<std::endl; |
} |
} |
|
void Generator::writeMasterDemux(std::ostream &os) { |
os<<"-- MASTER->SLAVE DEMUX"<<std::endl; |
os<<std::endl; |
|
if(slaves()>1) { |
os<<"select_slave<="; |
for(int i=0;i<slaves();i++) { |
if(i>0) os<<'\t'; |
os<<decodedLiteral(i,_slavesRange.length()); |
os<<" when adr_mux("<<_slaveDecoderRange.toString()<<")="; |
os<<binaryLiteral(i,_slaveDecoderRange.length())<<" else"<<std::endl; |
} |
if(_fallbackSlave) { |
os<<'\t'<<decodedLiteral(slaves(),_slavesRange.length())<< |
"; -- fallback slave"<<std::endl; |
} |
else os<<"\t(others=>'-');"<<std::endl; |
os<<std::endl; |
|
for(int i=0;i<slaves();i++) { |
os<<'m'<<i<<"_cyc_o<=cyc_mux and select_slave("<<i<<");"<<std::endl; |
os<<'m'<<i<<"_stb_o<=stb_mux and select_slave("<<i<<");"<<std::endl; |
os<<'m'<<i<<"_we_o<=we_mux;"<<std::endl; |
if(_selRange.valid()) os<<'m'<<i<<"_sel_o<=sel_mux;"<<std::endl; |
if(registeredFeedback()) { |
os<<'m'<<i<<"_cti_o<=cti_mux;"<<std::endl; |
os<<'m'<<i<<"_bte_o<=bte_mux;"<<std::endl; |
} |
os<<'m'<<i<<"_adr_o<=adr_mux(m"<<i<<"_adr_o'range);"<<std::endl; |
os<<'m'<<i<<"_dat_o<=wdata_mux;"<<std::endl; |
os<<std::endl; |
} |
} |
else { // just one slave |
os<<"m0_cyc_o<=cyc_mux;"<<std::endl; |
os<<"m0_stb_o<=stb_mux;"<<std::endl; |
os<<"m0_we_o<=we_mux;"<<std::endl; |
if(_selRange.valid()) os<<"m0_sel_o<=sel_mux;"<<std::endl; |
if(registeredFeedback()) { |
os<<"m0_cti_o<=cti_mux;"<<std::endl; |
os<<"m0_bte_o<=bte_mux;"<<std::endl; |
} |
os<<"m0_adr_o<=adr_mux(m0_adr_o'range);"<<std::endl; |
os<<"m0_dat_o<=wdata_mux;"<<std::endl; |
os<<std::endl; |
} |
} |
|
void Generator::writeSlaveMux(std::ostream &os) { |
os<<"-- SLAVE->MASTER MUX"<<std::endl; |
os<<std::endl; |
|
if(slaves()>1) { |
os<<"ack_mux<="; |
for(int i=0;i<slaves();i++) { |
if(i>0) os<<'\t'; |
os<<"(m"<<i<<"_ack_i and select_slave("<<i<<"))"; |
if(i<slaves()-1||_fallbackSlave) os<<" or"<<std::endl; |
else os<<';'<<std::endl; |
} |
if(_fallbackSlave) { |
os<<"\t(cyc_mux and stb_mux and select_slave("<<slaves()<< |
")); -- fallback slave"<<std::endl; |
} |
os<<std::endl; |
|
os<<"rdata_mux_gen: for i in rdata_mux'range generate"<<std::endl; |
os<<"\trdata_mux(i)<="; |
for(int i=0;i<slaves();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(m"<<i<<"_dat_i(i) and select_slave("<<i<<"))"; |
if(i<slaves()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
} |
else { // just one slave |
os<<"ack_mux<=m0_ack_i;"<<std::endl; |
os<<"rdata_mux<=m0_dat_i;"<<std::endl; |
os<<std::endl; |
} |
} |
|
void Generator::writeSlaveDemux(std::ostream &os) { |
os<<"-- SLAVE->MASTER DEMUX"<<std::endl; |
os<<std::endl; |
|
if(masters()>1) { |
for(int i=0;i<masters();i++) { |
os<<'s'<<i<<"_ack_o<=ack_mux and grant("<<i<<");"<<std::endl; |
os<<'s'<<i<<"_dat_o<=rdata_mux;"<<std::endl; |
os<<std::endl; |
} |
} |
else { // just one master |
os<<"s0_ack_o<=ack_mux;"<<std::endl; |
os<<"s0_dat_o<=rdata_mux;"<<std::endl; |
os<<std::endl; |
} |
} |
|
std::string Generator::binaryLiteral(int value,int n) { |
std::ostringstream oss; |
oss.put('\"'); |
for(int i=n-1;i>=0;i--) { |
if(value>=static_cast<int>(sizeof(int)*8)) oss.put('0'); |
else if((value>>i)&1) oss.put('1'); |
else oss.put('0'); |
} |
oss.put('\"'); |
return oss.str(); |
} |
|
std::string Generator::decodedLiteral(int value,int n) { |
std::ostringstream oss; |
oss.put('\"'); |
for(int i=n-1;i>=0;i--) { |
if(value==i) oss.put('1'); |
else oss.put('0'); |
} |
oss.put('\"'); |
return oss.str(); |
} |
/wigen/generator.h
1,86 → 1,86
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Generator class which generates |
* WISHBONE interconnect VHDL description based on provided |
* parameters. |
*/ |
|
#ifndef GENERATOR_H_INCLUDED |
#define GENERATOR_H_INCLUDED |
|
#include "range.h" |
|
#include <iostream> |
#include <string> |
|
class Generator { |
int _masters; |
int _slaves; |
int _addrWidth; |
int _slaveAddrWidth; |
int _portSize; |
int _portGranularity; |
|
std::string _entityName; |
bool _pipelinedArbiter; |
bool _registeredFeedback; |
bool _unsafeDecoder; |
|
Range _mastersRange; |
Range _slavesRange; |
Range _addrRange; |
Range _slaveAddrRange; |
Range _slaveDecoderRange; |
Range _dataRange; |
Range _selRange; |
|
bool _fallbackSlave; |
|
public: |
Generator(); |
|
void setMasters(int i); |
void setSlaves(int i); |
void setAddrWidth(int i); |
void setSlaveAddrWidth(int i); |
void setPortSize(int i); |
void setPortGranularity(int i); |
void setEntityName(const std::string &str); |
void setPipelinedArbiter(bool b); |
void setRegisteredFeedback(bool b); |
void setUnsafeDecoder(bool b); |
|
int masters() const; |
int slaves() const; |
int addrWidth() const; |
int slaveAddrWidth() const; |
int portSize() const; |
int portGranularity() const; |
std::string entityName() const; |
bool pipelinedArbiter() const; |
bool registeredFeedback() const; |
bool unsafeDecoder() const; |
|
void generate(const std::string &filename); |
|
private: |
void prepare(); |
void writeBanner(std::ostream &os); |
void writePreamble(std::ostream &os); |
void writeEntity(std::ostream &os); |
void writeArchitecture(std::ostream &os); |
|
void writeArbiter(std::ostream &os); |
void writeMasterMux(std::ostream &os); |
void writeMasterDemux(std::ostream &os); |
void writeSlaveMux(std::ostream &os); |
void writeSlaveDemux(std::ostream &os); |
|
static std::string binaryLiteral(int value,int n); |
static std::string decodedLiteral(int value,int n); |
}; |
|
#endif |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Generator class which generates |
* WISHBONE interconnect VHDL description based on provided |
* parameters. |
*/ |
|
#ifndef GENERATOR_H_INCLUDED |
#define GENERATOR_H_INCLUDED |
|
#include "range.h" |
|
#include <iostream> |
#include <string> |
|
class Generator { |
int _masters; |
int _slaves; |
int _addrWidth; |
int _slaveAddrWidth; |
int _portSize; |
int _portGranularity; |
|
std::string _entityName; |
bool _pipelinedArbiter; |
bool _registeredFeedback; |
bool _unsafeDecoder; |
|
Range _mastersRange; |
Range _slavesRange; |
Range _addrRange; |
Range _slaveAddrRange; |
Range _slaveDecoderRange; |
Range _dataRange; |
Range _selRange; |
|
bool _fallbackSlave; |
|
public: |
Generator(); |
|
void setMasters(int i); |
void setSlaves(int i); |
void setAddrWidth(int i); |
void setSlaveAddrWidth(int i); |
void setPortSize(int i); |
void setPortGranularity(int i); |
void setEntityName(const std::string &str); |
void setPipelinedArbiter(bool b); |
void setRegisteredFeedback(bool b); |
void setUnsafeDecoder(bool b); |
|
int masters() const; |
int slaves() const; |
int addrWidth() const; |
int slaveAddrWidth() const; |
int portSize() const; |
int portGranularity() const; |
std::string entityName() const; |
bool pipelinedArbiter() const; |
bool registeredFeedback() const; |
bool unsafeDecoder() const; |
|
void generate(const std::string &filename); |
|
private: |
void prepare(); |
void writeBanner(std::ostream &os); |
void writePreamble(std::ostream &os); |
void writeEntity(std::ostream &os); |
void writeArchitecture(std::ostream &os); |
|
void writeArbiter(std::ostream &os); |
void writeMasterMux(std::ostream &os); |
void writeMasterDemux(std::ostream &os); |
void writeSlaveMux(std::ostream &os); |
void writeSlaveDemux(std::ostream &os); |
|
static std::string binaryLiteral(int value,int n); |
static std::string decodedLiteral(int value,int n); |
}; |
|
#endif |
/wigen/range.cpp
1,52 → 1,52
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Range class. |
*/ |
|
#include "range.h" |
|
#include <stdexcept> |
|
Range::Range(): _valid(false) {} |
|
Range::Range(int h,int l): _high(h),_low(l),_valid(true) { |
if(l>h) throw std::runtime_error("Invalid range"); |
} |
|
void Range::assign(int h,int l) { |
if(l>h) throw std::runtime_error("Invalid range"); |
_high=h; |
_low=l; |
_valid=true; |
} |
|
void Range::clear() { |
_valid=false; |
} |
|
bool Range::valid() const { |
return _valid; |
} |
|
int Range::high() const { |
if(!_valid) throw std::runtime_error("Invalid range"); |
return _high; |
} |
|
int Range::low() const { |
if(!_valid) throw std::runtime_error("Invalid range"); |
return _low; |
} |
|
int Range::length() const { |
if(!_valid) throw std::runtime_error("Invalid range"); |
return _high-_low+1; |
} |
|
std::string Range::toString() const { |
if(!_valid) throw std::runtime_error("Invalid range"); |
return std::to_string(_high)+" downto "+std::to_string(_low); |
} |
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Range class. |
*/ |
|
#include "range.h" |
|
#include <stdexcept> |
|
Range::Range(): _valid(false) {} |
|
Range::Range(int h,int l): _high(h),_low(l),_valid(true) { |
if(l>h) throw std::runtime_error("Invalid range"); |
} |
|
void Range::assign(int h,int l) { |
if(l>h) throw std::runtime_error("Invalid range"); |
_high=h; |
_low=l; |
_valid=true; |
} |
|
void Range::clear() { |
_valid=false; |
} |
|
bool Range::valid() const { |
return _valid; |
} |
|
int Range::high() const { |
if(!_valid) throw std::runtime_error("Invalid range"); |
return _high; |
} |
|
int Range::low() const { |
if(!_valid) throw std::runtime_error("Invalid range"); |
return _low; |
} |
|
int Range::length() const { |
if(!_valid) throw std::runtime_error("Invalid range"); |
return _high-_low+1; |
} |
|
std::string Range::toString() const { |
if(!_valid) throw std::runtime_error("Invalid range"); |
return std::to_string(_high)+" downto "+std::to_string(_low); |
} |
/wigen/CMakeLists.txt
1,7 → 1,7
cmake_minimum_required(VERSION 3.3.0) |
|
add_executable(wigen generator.cpp main.cpp range.cpp) |
|
# Install |
|
install(TARGETS wigen DESTINATION .) |
cmake_minimum_required(VERSION 3.3.0) |
|
add_executable(wigen generator.cpp main.cpp range.cpp) |
|
# Install |
|
install(TARGETS wigen DESTINATION .) |
/CMakeLists.txt
1,50 → 1,50
cmake_minimum_required(VERSION 3.3.0) |
project(lxp32tools) |
|
# Examine environment |
|
if(CMAKE_C_COMPILER_ID STREQUAL GNU OR CMAKE_C_COMPILER_ID STREQUAL Clang) |
set(GNU_SYNTAX TRUE) |
endif() |
|
# Set default install prefix if not manually set by the user |
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) |
set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/../bin" CACHE PATH "Install prefix" FORCE) |
endif() |
|
message("Install prefix: ${CMAKE_INSTALL_PREFIX}") |
|
# Enable C++11 |
|
set(CMAKE_CXX_STANDARD 11) |
|
# Set up warning level for GCC/Clang |
|
if(GNU_SYNTAX) |
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic -Wall -Wextra") |
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra") |
endif() |
|
# On Windows, link runtime statically |
|
if(WIN32) |
if(GNU_SYNTAX) |
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") |
elseif(MSVC) |
foreach(flag_var |
CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE |
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO |
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE |
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO |
) |
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") |
endforeach(flag_var) |
endif() |
endif() |
|
# Build targets |
|
add_subdirectory(lxp32asm) |
add_subdirectory(lxp32dump) |
add_subdirectory(wigen) |
cmake_minimum_required(VERSION 3.3.0) |
project(lxp32tools) |
|
# Examine environment |
|
if(CMAKE_C_COMPILER_ID STREQUAL GNU OR CMAKE_C_COMPILER_ID STREQUAL Clang) |
set(GNU_SYNTAX TRUE) |
endif() |
|
# Set default install prefix if not manually set by the user |
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) |
set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/../bin" CACHE PATH "Install prefix" FORCE) |
endif() |
|
message("Install prefix: ${CMAKE_INSTALL_PREFIX}") |
|
# Enable C++11 |
|
set(CMAKE_CXX_STANDARD 11) |
|
# Set up warning level for GCC/Clang |
|
if(GNU_SYNTAX) |
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic -Wall -Wextra") |
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra") |
endif() |
|
# On Windows, link runtime statically |
|
if(WIN32) |
if(GNU_SYNTAX) |
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") |
elseif(MSVC) |
foreach(flag_var |
CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE |
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO |
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE |
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO |
) |
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") |
endforeach(flag_var) |
endif() |
endif() |
|
# Build targets |
|
add_subdirectory(lxp32asm) |
add_subdirectory(lxp32dump) |
add_subdirectory(wigen) |