Line 28... |
Line 28... |
|
|
_line=0;
|
_line=0;
|
_state=Initial;
|
_state=Initial;
|
_currentFileName=filename;
|
_currentFileName=filename;
|
processFileRecursive(filename);
|
processFileRecursive(filename);
|
|
|
// 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+"\"";
|
msg<<" (referenced from "<<sym.second.refs[0].source;
|
msg<<" (referenced from "<<sym.second.refs[0].source;
|
msg<<":"<<sym.second.refs[0].line<<")";
|
msg<<":"<<sym.second.refs[0].line<<")";
|
throw std::runtime_error(msg.str());
|
throw std::runtime_error(msg.str());
|
}
|
}
|
}
|
}
|
|
|
|
for(auto const &sym: _exportedSymbols) _obj.exportSymbol(sym);
|
}
|
}
|
|
|
void Assembler::processFileRecursive(const std::string &filename) {
|
void Assembler::processFileRecursive(const std::string &filename) {
|
std::ifstream in(filename,std::ios_base::in);
|
std::ifstream in(filename,std::ios_base::in);
|
if(!in) throw std::runtime_error("Cannot open file \""+filename+"\"");
|
if(!in) throw std::runtime_error("Cannot open file \""+filename+"\"");
|
Line 61... |
Line 64... |
expand(tokens);
|
expand(tokens);
|
elaborate(tokens);
|
elaborate(tokens);
|
_line++;
|
_line++;
|
}
|
}
|
|
|
|
if(_state!=Initial) throw std::runtime_error("Unexpected end of file");
|
|
|
_line=savedLine;
|
_line=savedLine;
|
_state=savedState;
|
_state=savedState;
|
_currentFileName=savedFileName;
|
_currentFileName=savedFileName;
|
|
|
for(auto const &label: _currentLabels) {
|
if(!_currentLabels.empty())
|
_obj.addLocalSymbol(label,
|
throw std::runtime_error("Symbol definition must be followed by an instruction or data definition statement");
|
static_cast<LinkableObject::Word>(_obj.codeSize()));
|
|
}
|
|
|
|
_currentLabels.clear();
|
|
}
|
}
|
|
|
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 125... |
Line 126... |
else throw std::runtime_error(std::string("Unexpected character: \"")+ch+"\"");
|
else throw std::runtime_error(std::string("Unexpected character: \"")+ch+"\"");
|
}
|
}
|
else throw std::runtime_error(std::string("Unexpected character: \"")+ch+"\"");
|
else throw std::runtime_error(std::string("Unexpected character: \"")+ch+"\"");
|
break;
|
break;
|
case Word:
|
case Word:
|
if(std::isalnum(ch)||ch=='_'||ch=='@') word+=ch;
|
if(std::isalnum(ch)||ch=='_'||ch=='@'||ch=='+'||ch=='-') word+=ch;
|
else {
|
else {
|
i--;
|
i--;
|
_state=Initial;
|
_state=Initial;
|
tokenList.push_back(std::move(word));
|
tokenList.push_back(std::move(word));
|
}
|
}
|
Line 204... |
Line 205... |
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);
|
if(it==_macros.end()) newlist.push_back(std::move(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);
|
else for(auto const &replace: it->second) newlist.push_back(replace);
|
}
|
}
|
list=std::move(newlist);
|
list=std::move(newlist);
|
}
|
}
|
|
|
Line 231... |
Line 237... |
LinkableObject::Word rva;
|
LinkableObject::Word rva;
|
if(list[0][0]=='.') rva=elaborateDataDefinition(list);
|
if(list[0][0]=='.') rva=elaborateDataDefinition(list);
|
else rva=elaborateInstruction(list);
|
else rva=elaborateInstruction(list);
|
|
|
for(auto const &label: _currentLabels) {
|
for(auto const &label: _currentLabels) {
|
_obj.addLocalSymbol(label,rva);
|
_obj.addSymbol(label,rva);
|
}
|
}
|
_currentLabels.clear();
|
_currentLabels.clear();
|
}
|
}
|
}
|
}
|
|
|
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) throw std::runtime_error("Wrong number of tokens in the directive");
|
if(list.size()<3)
|
if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\"");
|
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()));
|
_macros.emplace(list[1],TokenList(list.begin()+2,list.end()));
|
}
|
}
|
else if(list[0]=="#extern") {
|
else if(list[0]=="#export") {
|
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive");
|
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]+"\"");
|
if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\"");
|
_obj.addExternalSymbol(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") {
|
else if(list[0]=="#include") {
|
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive");
|
if(list.size()!=2) 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);
|
Line 283... |
Line 298... |
|
|
if(list[0]==".align") {
|
if(list[0]==".align") {
|
if(list.size()>2) throw std::runtime_error("Unexpected token: \""+list[2]+"\"");
|
if(list.size()>2) throw std::runtime_error("Unexpected token: \""+list[2]+"\"");
|
std::size_t align=4;
|
std::size_t align=4;
|
if(list.size()>1) align=static_cast<std::size_t>(numericLiteral(list[1]));
|
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);
|
rva=_obj.addPadding(align);
|
}
|
}
|
else if(list[0]==".reserve") {
|
else if(list[0]==".reserve") {
|
if(list.size()<2) throw std::runtime_error("Unexpected end of statement");
|
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]+"\"");
|
else if(list.size()>2) throw std::runtime_error("Unexpected token: \""+list[2]+"\"");
|
Line 349... |
Line 366... |
else if(list[0]=="divu") encodeDivu(list);
|
else if(list[0]=="divu") encodeDivu(list);
|
else if(list[0]=="hlt") encodeHlt(list);
|
else if(list[0]=="hlt") encodeHlt(list);
|
else if(list[0]=="jmp") encodeJmp(list);
|
else if(list[0]=="jmp") encodeJmp(list);
|
else if(list[0]=="iret") encodeIret(list);
|
else if(list[0]=="iret") encodeIret(list);
|
else if(list[0]=="lc") encodeLc(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]=="lsb") encodeLsb(list);
|
else if(list[0]=="lub") encodeLub(list);
|
else if(list[0]=="lub") encodeLub(list);
|
else if(list[0]=="lw") encodeLw(list);
|
else if(list[0]=="lw") encodeLw(list);
|
else if(list[0]=="mods") encodeMods(list);
|
else if(list[0]=="mods") encodeMods(list);
|
else if(list[0]=="modu") encodeModu(list);
|
else if(list[0]=="modu") encodeModu(list);
|
else if(list[0]=="mov") encodeMov(list);
|
else if(list[0]=="mov") encodeMov(list);
|
else if(list[0]=="mul") encodeMul(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]=="nop") encodeNop(list);
|
else if(list[0]=="not") encodeNot(list);
|
else if(list[0]=="not") encodeNot(list);
|
else if(list[0]=="or") encodeOr(list);
|
else if(list[0]=="or") encodeOr(list);
|
else if(list[0]=="ret") encodeRet(list);
|
else if(list[0]=="ret") encodeRet(list);
|
else if(list[0]=="sb") encodeSb(list);
|
else if(list[0]=="sb") encodeSb(list);
|
Line 454... |
Line 473... |
a.type=Operand::Register;
|
a.type=Operand::Register;
|
a.reg=252;
|
a.reg=252;
|
arglist.push_back(std::move(a));
|
arglist.push_back(std::move(a));
|
}
|
}
|
else if(list[i].size()==3&&list[i].substr(0,2)=="iv"&&
|
else if(list[i].size()==3&&list[i].substr(0,2)=="iv"&&
|
std::isdigit(list[i][2])) // interrupt vector
|
list[i][2]>='0'&&list[i][2]<='7') // interrupt vector
|
{
|
{
|
a.type=Operand::Register;
|
a.type=Operand::Register;
|
a.reg=240+(list[i][2]-'0');
|
a.reg=240+(list[i][2]-'0');
|
arglist.push_back(std::move(a));
|
arglist.push_back(std::move(a));
|
}
|
}
|
Line 596... |
Line 615... |
}
|
}
|
|
|
void Assembler::encodeDivs(const TokenList &list) {
|
void Assembler::encodeDivs(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("divs instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("divs instruction requires 3 operands");
|
if(args[2].type==Operand::NumericLiteral&&args[2].i==0) {
|
|
std::cerr<<currentFileName()<<":"<<line()<<": ";
|
|
std::cerr<<"Warning: Division by zero"<<std::endl;
|
|
}
|
|
|
|
LinkableObject::Word w=0x54000000;
|
LinkableObject::Word w=0x54000000;
|
encodeDstOperand(w,args[0]);
|
encodeDstOperand(w,args[0]);
|
encodeRd1Operand(w,args[1]);
|
encodeRd1Operand(w,args[1]);
|
encodeRd2Operand(w,args[2]);
|
encodeRd2Operand(w,args[2]);
|
_obj.addWord(w);
|
_obj.addWord(w);
|
}
|
}
|
|
|
void Assembler::encodeDivu(const TokenList &list) {
|
void Assembler::encodeDivu(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("divu instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("divu instruction requires 3 operands");
|
if(args[2].type==Operand::NumericLiteral&&args[2].i==0) {
|
|
std::cerr<<currentFileName()<<":"<<line()<<": ";
|
|
std::cerr<<"Warning: Division by zero"<<std::endl;
|
|
}
|
|
|
|
LinkableObject::Word w=0x50000000;
|
LinkableObject::Word w=0x50000000;
|
encodeDstOperand(w,args[0]);
|
encodeDstOperand(w,args[0]);
|
encodeRd1Operand(w,args[1]);
|
encodeRd1Operand(w,args[1]);
|
encodeRd2Operand(w,args[2]);
|
encodeRd2Operand(w,args[2]);
|
_obj.addWord(w);
|
_obj.addWord(w);
|
Line 654... |
Line 663... |
LinkableObject::Word w=0x04000000;
|
LinkableObject::Word w=0x04000000;
|
encodeDstOperand(w,args[0]);
|
encodeDstOperand(w,args[0]);
|
_obj.addWord(w);
|
_obj.addWord(w);
|
|
|
if(args[1].type==Operand::Identifier) {
|
if(args[1].type==Operand::Identifier) {
|
auto symRva=_obj.addWord(static_cast<LinkableObject::Word>(args[1].i));
|
LinkableObject::Reference ref;
|
_obj.addReference(args[1].str,currentFileName(),line(),symRva);
|
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) {
|
else if(args[1].type==Operand::NumericLiteral) {
|
_obj.addWord(static_cast<LinkableObject::Word>(args[1].i));
|
_obj.addWord(static_cast<LinkableObject::Word>(args[1].i));
|
}
|
}
|
else throw std::runtime_error("\""+args[1].str+"\": bad argument");
|
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) {
|
void Assembler::encodeLsb(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=2) throw std::runtime_error("lsb instruction requires 2 operands");
|
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");
|
if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register");
|
LinkableObject::Word w=0x2E000000;
|
LinkableObject::Word w=0x2E000000;
|
Line 733... |
Line 774... |
encodeRd1Operand(w,args[1]);
|
encodeRd1Operand(w,args[1]);
|
encodeRd2Operand(w,args[2]);
|
encodeRd2Operand(w,args[2]);
|
_obj.addWord(w);
|
_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) {
|
void Assembler::encodeNop(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(!args.empty()) throw std::runtime_error("nop instruction doesn't take operands");
|
if(!args.empty()) throw std::runtime_error("nop instruction doesn't take operands");
|
_obj.addWord(0);
|
_obj.addWord(0);
|
}
|
}
|