Line 29... |
Line 29... |
_line=0;
|
_line=0;
|
_state=Initial;
|
_state=Initial;
|
_currentFileName=filename;
|
_currentFileName=filename;
|
processFileRecursive(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
|
// Examine symbol table
|
for(auto const &sym: _obj.symbols()) {
|
for(auto const &sym: _obj.symbols()) {
|
if(sym.second.type==LinkableObject::Unknown&&!sym.second.refs.empty()) {
|
if(sym.second.type==LinkableObject::Unknown&&!sym.second.refs.empty()) {
|
std::ostringstream msg;
|
std::ostringstream msg;
|
msg<<"Undefined symbol \""+sym.first+"\"";
|
msg<<"Undefined symbol \""+sym.first+"\"";
|
Line 69... |
Line 75... |
if(_state!=Initial) throw std::runtime_error("Unexpected end of file");
|
if(_state!=Initial) throw std::runtime_error("Unexpected end of file");
|
|
|
_line=savedLine;
|
_line=savedLine;
|
_state=savedState;
|
_state=savedState;
|
_currentFileName=savedFileName;
|
_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) {
|
void Assembler::addIncludeSearchDir(const std::string &dir) {
|
auto ndir=Utils::normalizeSeparators(dir);
|
auto ndir=Utils::normalizeSeparators(dir);
|
if(!ndir.empty()&&ndir.back()!='/') ndir.push_back('/');
|
if(!ndir.empty()&&ndir.back()!='/') ndir.push_back('/');
|
Line 205... |
Line 208... |
void Assembler::expand(TokenList &list) {
|
void Assembler::expand(TokenList &list) {
|
TokenList newlist;
|
TokenList newlist;
|
// Perform macro substitution
|
// Perform macro substitution
|
for(auto &token: list) {
|
for(auto &token: list) {
|
auto it=_macros.find(token);
|
auto it=_macros.find(token);
|
// Note: we don't expand a macro identifier in the #define statement
|
bool substitute=false;
|
// since that would lead to counter-intuitive results
|
if(it!=_macros.end()) {
|
if(it==_macros.end()||
|
substitute=true;
|
(newlist.size()==1&&newlist[0]=="#define")||
|
// Don't substitute macros for a second token in certain directives
|
(newlist.size()==3&&newlist[1]==":"&&newlist[2]=="#define"))
|
if(newlist.size()==1) {
|
newlist.push_back(std::move(token));
|
if(newlist[0]=="#define") substitute=false;
|
else for(auto const &replace: it->second) newlist.push_back(replace);
|
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);
|
list=std::move(newlist);
|
}
|
}
|
|
|
void Assembler::elaborate(TokenList &list) {
|
void Assembler::elaborate(TokenList &list) {
|
Line 223... |
Line 239... |
|
|
// Process label (if present)
|
// Process label (if present)
|
if(list.size()>=2&&list[1]==":") {
|
if(list.size()>=2&&list[1]==":") {
|
if(!validateIdentifier(list[0]))
|
if(!validateIdentifier(list[0]))
|
throw std::runtime_error("Ill-formed identifier: \""+list[0]+"\"");
|
throw std::runtime_error("Ill-formed identifier: \""+list[0]+"\"");
|
_currentLabels.push_back(std::move(list[0]));
|
if(isSectionEnabled()) _currentLabels.push_back(std::move(list[0]));
|
list.erase(list.begin(),list.begin()+2);
|
list.erase(list.begin(),list.begin()+2);
|
}
|
}
|
|
|
if(list.empty()) return;
|
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
|
// Process statement itself
|
if(list[0][0]=='#') elaborateDirective(list);
|
if(list[0][0]=='#') elaborateDirective(list);
|
else {
|
else {
|
LinkableObject::Word rva;
|
LinkableObject::Word rva;
|
if(list[0][0]=='.') rva=elaborateDataDefinition(list);
|
if(list[0][0]=='.') rva=elaborateDataDefinition(list);
|
Line 247... |
Line 267... |
|
|
void Assembler::elaborateDirective(TokenList &list) {
|
void Assembler::elaborateDirective(TokenList &list) {
|
assert(!list.empty());
|
assert(!list.empty());
|
|
|
if(list[0]=="#define") {
|
if(list[0]=="#define") {
|
if(list.size()<3)
|
if(list.size()<2)
|
throw std::runtime_error("Wrong number of tokens in the directive");
|
throw std::runtime_error("Wrong number of tokens in the directive");
|
if(_macros.find(list[1])!=_macros.end())
|
if(_macros.find(list[1])!=_macros.end())
|
throw std::runtime_error("Macro \""+list[1]+"\" has been already defined");
|
throw std::runtime_error("Macro \""+list[1]+"\" has been already defined");
|
if(!validateIdentifier(list[1]))
|
if(!validateIdentifier(list[1]))
|
throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\"");
|
throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\"");
|
_macros.emplace(list[1],TokenList(list.begin()+2,list.end()));
|
_macros.emplace(list[1],TokenList(list.begin()+2,list.end()));
|
}
|
}
|
else if(list[0]=="#export") {
|
else if(list[0]=="#export") {
|
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive");
|
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]+"\"");
|
if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\"");
|
_exportedSymbols.push_back(list[1]);
|
_exportedSymbols.push_back(list[1]);
|
}
|
}
|
else if(list[0]=="#import") {
|
else if(list[0]=="#import") {
|
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive");
|
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]+"\"");
|
if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\"");
|
_obj.addImportedSymbol(list[1]);
|
_obj.addImportedSymbol(list[1]);
|
}
|
}
|
else if(list[0]=="#include") {
|
else if(list[0]=="#include") {
|
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive");
|
if(list.size()!=2) throw std::runtime_error("Wrong number of tokens in the directive");
|
auto filename=Utils::dequoteString(list[1]);
|
auto filename=Utils::dequoteString(list[1]);
|
if(Utils::isAbsolutePath(filename)) return processFileRecursive(filename);
|
if(Utils::isAbsolutePath(filename)) return processFileRecursive(filename);
|
else {
|
else {
|
auto path=Utils::relativePath(currentFileName(),filename);
|
auto path=Utils::relativePath(currentFileName(),filename);
|
if(Utils::fileExists(path)) return processFileRecursive(path);
|
if(Utils::fileExists(path)) return processFileRecursive(path);
|
Line 282... |
Line 302... |
}
|
}
|
}
|
}
|
throw std::runtime_error("Cannot locate include file \""+filename+"\"");
|
throw std::runtime_error("Cannot locate include file \""+filename+"\"");
|
}
|
}
|
else if(list[0]=="#message") {
|
else if(list[0]=="#message") {
|
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive");
|
if(list.size()!=2) throw std::runtime_error("Wrong number of tokens in the directive");
|
auto msg=Utils::dequoteString(list[1]);
|
auto msg=Utils::dequoteString(list[1]);
|
std::cout<<currentFileName()<<":"<<line()<<": "<<msg<<std::endl;
|
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]+"\"");
|
else throw std::runtime_error("Unrecognized directive: \""+list[0]+"\"");
|
}
|
}
|
|
|
LinkableObject::Word Assembler::elaborateDataDefinition(TokenList &list) {
|
LinkableObject::Word Assembler::elaborateDataDefinition(TokenList &list) {
|
assert(!list.empty());
|
assert(!list.empty());
|
Line 390... |
Line 435... |
else if(list[0]=="xor") encodeXor(list);
|
else if(list[0]=="xor") encodeXor(list);
|
else throw std::runtime_error("Unrecognized instruction: \""+list[0]+"\"");
|
else throw std::runtime_error("Unrecognized instruction: \""+list[0]+"\"");
|
return rva;
|
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) {
|
bool Assembler::validateIdentifier(const std::string &str) {
|
/*
|
/*
|
* Valid identifier must satisfy the following requirements:
|
* Valid identifier must satisfy the following requirements:
|
* 1. Must not be empty
|
* 1. Must not be empty
|
* 2. The first character must be either alphabetic or an underscore
|
* 2. The first character must be either alphabetic or an underscore
|