/*
|
/*
|
* Copyright (c) 2016 by Alex I. Kuznetsov.
|
* Copyright (c) 2016 by Alex I. Kuznetsov.
|
*
|
*
|
* Part of the LXP32 CPU IP core.
|
* Part of the LXP32 CPU IP core.
|
*
|
*
|
* This module implements members of the Assembler class.
|
* This module implements members of the Assembler class.
|
*/
|
*/
|
|
|
#include "assembler.h"
|
#include "assembler.h"
|
#include "utils.h"
|
#include "utils.h"
|
|
|
#include <iostream>
|
#include <iostream>
|
#include <fstream>
|
#include <fstream>
|
#include <sstream>
|
#include <sstream>
|
#include <stdexcept>
|
#include <stdexcept>
|
#include <utility>
|
#include <utility>
|
#include <limits>
|
#include <limits>
|
#include <type_traits>
|
#include <type_traits>
|
#include <cctype>
|
#include <cctype>
|
#include <cassert>
|
#include <cassert>
|
#include <cstdlib>
|
#include <cstdlib>
|
|
|
void Assembler::processFile(const std::string &filename) {
|
void Assembler::processFile(const std::string &filename) {
|
auto nativePath=Utils::normalizeSeparators(filename);
|
auto nativePath=Utils::normalizeSeparators(filename);
|
auto pos=nativePath.find_last_of('/');
|
auto pos=nativePath.find_last_of('/');
|
if(pos!=std::string::npos) nativePath=filename.substr(pos+1);
|
if(pos!=std::string::npos) nativePath=filename.substr(pos+1);
|
_obj.setName(nativePath);
|
_obj.setName(nativePath);
|
|
|
_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+"\"";
|
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);
|
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+"\"");
|
|
|
// Process input file line-by-line
|
// Process input file line-by-line
|
auto savedLine=_line;
|
auto savedLine=_line;
|
auto savedState=_state;
|
auto savedState=_state;
|
auto savedFileName=_currentFileName;
|
auto savedFileName=_currentFileName;
|
|
|
_line=1;
|
_line=1;
|
_state=Initial;
|
_state=Initial;
|
_currentFileName=filename;
|
_currentFileName=filename;
|
|
|
std::string line;
|
std::string line;
|
while(std::getline(in,line)) {
|
while(std::getline(in,line)) {
|
auto tokens=tokenize(line);
|
auto tokens=tokenize(line);
|
expand(tokens);
|
expand(tokens);
|
elaborate(tokens);
|
elaborate(tokens);
|
_line++;
|
_line++;
|
}
|
}
|
|
|
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('/');
|
_includeSearchDirs.push_back(std::move(ndir));
|
_includeSearchDirs.push_back(std::move(ndir));
|
}
|
}
|
|
|
int Assembler::line() const {
|
int Assembler::line() const {
|
return _line;
|
return _line;
|
}
|
}
|
|
|
std::string Assembler::currentFileName() const {
|
std::string Assembler::currentFileName() const {
|
return _currentFileName;
|
return _currentFileName;
|
}
|
}
|
|
|
LinkableObject &Assembler::object() {
|
LinkableObject &Assembler::object() {
|
return _obj;
|
return _obj;
|
}
|
}
|
|
|
const LinkableObject &Assembler::object() const {
|
const LinkableObject &Assembler::object() const {
|
return _obj;
|
return _obj;
|
}
|
}
|
|
|
Assembler::TokenList Assembler::tokenize(const std::string &str) {
|
Assembler::TokenList Assembler::tokenize(const std::string &str) {
|
TokenList tokenList;
|
TokenList tokenList;
|
std::string word;
|
std::string word;
|
std::size_t i;
|
std::size_t i;
|
for(i=0;i<str.size();i++) {
|
for(i=0;i<str.size();i++) {
|
char ch=str[i];
|
char ch=str[i];
|
switch(_state) {
|
switch(_state) {
|
case Initial:
|
case Initial:
|
if(ch==' '||ch=='\t'||ch=='\n'||ch=='\r') continue; // skip whitespace
|
if(ch==' '||ch=='\t'||ch=='\n'||ch=='\r') continue; // skip whitespace
|
else if(ch==','||ch==':') { // separator
|
else if(ch==','||ch==':') { // separator
|
tokenList.push_back(std::string(1,ch));
|
tokenList.push_back(std::string(1,ch));
|
}
|
}
|
else if(std::isalnum(ch)||ch=='.'||ch=='#'||ch=='_'||ch=='-'||ch=='+') {
|
else if(std::isalnum(ch)||ch=='.'||ch=='#'||ch=='_'||ch=='-'||ch=='+') {
|
word=std::string(1,ch);
|
word=std::string(1,ch);
|
_state=Word;
|
_state=Word;
|
}
|
}
|
else if(ch=='\"') {
|
else if(ch=='\"') {
|
word="\"";
|
word="\"";
|
_state=StringLiteral;
|
_state=StringLiteral;
|
}
|
}
|
else if(ch=='/') {
|
else if(ch=='/') {
|
if(++i>=str.size()) throw std::runtime_error("Unexpected end of line");
|
if(++i>=str.size()) throw std::runtime_error("Unexpected end of line");
|
ch=str[i];
|
ch=str[i];
|
if(ch=='/') i=str.size(); // skip the rest of the line
|
if(ch=='/') i=str.size(); // skip the rest of the line
|
else if(ch=='*') _state=BlockComment;
|
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+"\"");
|
}
|
}
|
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=='@'||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));
|
}
|
}
|
break;
|
break;
|
case StringLiteral:
|
case StringLiteral:
|
if(ch=='\\') {
|
if(ch=='\\') {
|
if(++i>=str.size()) throw std::runtime_error("Unexpected end of line");
|
if(++i>=str.size()) throw std::runtime_error("Unexpected end of line");
|
ch=str[i];
|
ch=str[i];
|
if(ch=='\\') word.push_back('\\');
|
if(ch=='\\') word.push_back('\\');
|
else if(ch=='\"') word.push_back('\"');
|
else 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=='t') word.push_back('\t');
|
else if(ch=='n') word.push_back('\n');
|
else if(ch=='n') word.push_back('\n');
|
else if(ch=='r') word.push_back('\r');
|
else if(ch=='r') word.push_back('\r');
|
else if(ch=='x') { // hexadecimal sequence can be 1-2 digit long
|
else if(ch=='x') { // hexadecimal sequence can be 1-2 digit long
|
std::string seq;
|
std::string seq;
|
if(i+1<str.size()&&Utils::ishexdigit(str[i+1])) seq+=str[i+1];
|
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(i+2<str.size()&&Utils::ishexdigit(str[i+2])) seq+=str[i+2];
|
if(seq.empty()) throw std::runtime_error("Ill-formed escape sequence");
|
if(seq.empty()) throw std::runtime_error("Ill-formed escape sequence");
|
try {
|
try {
|
word.push_back(static_cast<char>(std::stoul(seq,nullptr,16)));
|
word.push_back(static_cast<char>(std::stoul(seq,nullptr,16)));
|
}
|
}
|
catch(std::exception &) {
|
catch(std::exception &) {
|
throw std::runtime_error("Ill-formed escape sequence");
|
throw std::runtime_error("Ill-formed escape sequence");
|
}
|
}
|
i+=seq.size();
|
i+=seq.size();
|
}
|
}
|
else if(Utils::isoctdigit(ch)) { // octal sequence can be 1-3 digit long
|
else if(Utils::isoctdigit(ch)) { // octal sequence can be 1-3 digit long
|
std::string seq(1,ch);
|
std::string seq(1,ch);
|
if(i+1<str.size()&&Utils::isoctdigit(str[i+1])) seq+=str[i+1];
|
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];
|
if(i+2<str.size()&&Utils::isoctdigit(str[i+2])) seq+=str[i+2];
|
unsigned long value;
|
unsigned long value;
|
try {
|
try {
|
value=std::stoul(seq,nullptr,8);
|
value=std::stoul(seq,nullptr,8);
|
}
|
}
|
catch(std::exception &) {
|
catch(std::exception &) {
|
throw std::runtime_error("Ill-formed escape sequence");
|
throw std::runtime_error("Ill-formed escape sequence");
|
}
|
}
|
|
|
if(value>255) throw std::runtime_error("Octal value is out of range");
|
if(value>255) throw std::runtime_error("Octal value is out of range");
|
word.push_back(static_cast<char>(value));
|
word.push_back(static_cast<char>(value));
|
|
|
i+=seq.size()-1;
|
i+=seq.size()-1;
|
}
|
}
|
else throw std::runtime_error(std::string("Unknown escape sequence: \"\\")+ch+"\"");
|
else throw std::runtime_error(std::string("Unknown escape sequence: \"\\")+ch+"\"");
|
}
|
}
|
else if(ch=='\"') {
|
else if(ch=='\"') {
|
word.push_back('\"');
|
word.push_back('\"');
|
tokenList.push_back(std::move(word));
|
tokenList.push_back(std::move(word));
|
_state=Initial;
|
_state=Initial;
|
}
|
}
|
else word.push_back(ch);
|
else word.push_back(ch);
|
break;
|
break;
|
case BlockComment:
|
case BlockComment:
|
if(ch=='*') {
|
if(ch=='*') {
|
if(++i>=str.size()) break;
|
if(++i>=str.size()) break;
|
ch=str[i];
|
ch=str[i];
|
if(ch=='/') _state=Initial;
|
if(ch=='/') _state=Initial;
|
else i--;
|
else i--;
|
}
|
}
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
if(_state==StringLiteral) throw std::runtime_error("Unexpected end of line");
|
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==Word) tokenList.push_back(std::move(word)); // store last word
|
if(_state!=BlockComment) _state=Initial; // reset state if not in block comment
|
if(_state!=BlockComment) _state=Initial; // reset state if not in block comment
|
|
|
return tokenList;
|
return tokenList;
|
}
|
}
|
|
|
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) {
|
if(list.empty()) return;
|
if(list.empty()) return;
|
|
|
// 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);
|
else rva=elaborateInstruction(list);
|
else rva=elaborateInstruction(list);
|
|
|
for(auto const &label: _currentLabels) {
|
for(auto const &label: _currentLabels) {
|
_obj.addSymbol(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)
|
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);
|
else {
|
else {
|
for(auto const &dir: _includeSearchDirs) {
|
for(auto const &dir: _includeSearchDirs) {
|
path=Utils::nativeSeparators(dir+filename);
|
path=Utils::nativeSeparators(dir+filename);
|
if(Utils::fileExists(path)) return processFileRecursive(path);
|
if(Utils::fileExists(path)) return processFileRecursive(path);
|
}
|
}
|
}
|
}
|
}
|
}
|
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());
|
|
|
LinkableObject::Word rva=0;
|
LinkableObject::Word rva=0;
|
|
|
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(!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");
|
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]+"\"");
|
auto n=static_cast<std::size_t>(numericLiteral(list[1]));
|
auto n=static_cast<std::size_t>(numericLiteral(list[1]));
|
rva=_obj.addZeros(n);
|
rva=_obj.addZeros(n);
|
}
|
}
|
else if(list[0]==".word") {
|
else if(list[0]==".word") {
|
if(list.size()<2) throw std::runtime_error("Unexpected end of statement");
|
if(list.size()<2) throw std::runtime_error("Unexpected end of statement");
|
for(std::size_t i=1;i<list.size();i++) {
|
for(std::size_t i=1;i<list.size();i++) {
|
if(i%2!=0) {
|
if(i%2!=0) {
|
auto w=static_cast<LinkableObject::Word>(numericLiteral(list[i]));
|
auto w=static_cast<LinkableObject::Word>(numericLiteral(list[i]));
|
auto r=_obj.addWord(w);
|
auto r=_obj.addWord(w);
|
if(i==1) rva=r;
|
if(i==1) rva=r;
|
}
|
}
|
else {
|
else {
|
if(list[i]!=",") throw std::runtime_error("Comma expected");
|
if(list[i]!=",") throw std::runtime_error("Comma expected");
|
if(i+1==list.size()) throw std::runtime_error("Unexpected end of statement");
|
if(i+1==list.size()) throw std::runtime_error("Unexpected end of statement");
|
}
|
}
|
}
|
}
|
}
|
}
|
else if(list[0]==".byte") {
|
else if(list[0]==".byte") {
|
if(list.size()<2) throw std::runtime_error("Unexpected end of statement");
|
if(list.size()<2) throw std::runtime_error("Unexpected end of statement");
|
for(std::size_t i=1;i<list.size();i++) {
|
for(std::size_t i=1;i<list.size();i++) {
|
if(i%2!=0) {
|
if(i%2!=0) {
|
if(list[i].at(0)=='\"') { // string literal
|
if(list[i].at(0)=='\"') { // string literal
|
auto bytes=Utils::dequoteString(list[i]);
|
auto bytes=Utils::dequoteString(list[i]);
|
auto r=_obj.addBytes(reinterpret_cast<const LinkableObject::Byte*>
|
auto r=_obj.addBytes(reinterpret_cast<const LinkableObject::Byte*>
|
(bytes.c_str()),bytes.size());
|
(bytes.c_str()),bytes.size());
|
if(i==1) rva=r;
|
if(i==1) rva=r;
|
}
|
}
|
else {
|
else {
|
auto n=numericLiteral(list[i]);
|
auto n=numericLiteral(list[i]);
|
|
|
if(n>255||n<-128) throw std::runtime_error("\""+list[i]+"\": out of range");
|
if(n>255||n<-128) throw std::runtime_error("\""+list[i]+"\": out of range");
|
|
|
auto b=static_cast<LinkableObject::Byte>(n);
|
auto b=static_cast<LinkableObject::Byte>(n);
|
auto r=_obj.addByte(b);
|
auto r=_obj.addByte(b);
|
if(i==1) rva=r;
|
if(i==1) rva=r;
|
}
|
}
|
}
|
}
|
else {
|
else {
|
if(list[i]!=",") throw std::runtime_error("Comma expected");
|
if(list[i]!=",") throw std::runtime_error("Comma expected");
|
if(i+1==list.size()) throw std::runtime_error("Unexpected end of statement");
|
if(i+1==list.size()) throw std::runtime_error("Unexpected end of statement");
|
}
|
}
|
}
|
}
|
}
|
}
|
else throw std::runtime_error("Unrecognized statement: \""+list[0]+"\"");
|
else throw std::runtime_error("Unrecognized statement: \""+list[0]+"\"");
|
|
|
return rva;
|
return rva;
|
}
|
}
|
|
|
LinkableObject::Word Assembler::elaborateInstruction(TokenList &list) {
|
LinkableObject::Word Assembler::elaborateInstruction(TokenList &list) {
|
assert(!list.empty());
|
assert(!list.empty());
|
auto rva=_obj.addPadding();
|
auto rva=_obj.addPadding();
|
if(list[0]=="add") encodeAdd(list);
|
if(list[0]=="add") encodeAdd(list);
|
else if(list[0]=="and") encodeAnd(list);
|
else if(list[0]=="and") encodeAnd(list);
|
else if(list[0]=="call") encodeCall(list);
|
else if(list[0]=="call") encodeCall(list);
|
else if(list[0].substr(0,4)=="cjmp") encodeCjmpxx(list);
|
else if(list[0].substr(0,4)=="cjmp") encodeCjmpxx(list);
|
else if(list[0]=="divs") encodeDivs(list);
|
else if(list[0]=="divs") encodeDivs(list);
|
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]=="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]=="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);
|
else if(list[0]=="sl") encodeSl(list);
|
else if(list[0]=="sl") encodeSl(list);
|
else if(list[0]=="srs") encodeSrs(list);
|
else if(list[0]=="srs") encodeSrs(list);
|
else if(list[0]=="sru") encodeSru(list);
|
else if(list[0]=="sru") encodeSru(list);
|
else if(list[0]=="sub") encodeSub(list);
|
else if(list[0]=="sub") encodeSub(list);
|
else if(list[0]=="sw") encodeSw(list);
|
else if(list[0]=="sw") encodeSw(list);
|
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
|
* 3. Subsequent characters must be either alphanumeric or underscores
|
* 3. Subsequent characters must be either alphanumeric or underscores
|
*/
|
*/
|
if(str.empty()) return false;
|
if(str.empty()) return false;
|
for(std::size_t i=0;i<str.size();i++) {
|
for(std::size_t i=0;i<str.size();i++) {
|
char ch=str[i];
|
char ch=str[i];
|
if(i==0) {
|
if(i==0) {
|
if(!std::isalpha(ch)&&ch!='_') return false;
|
if(!std::isalpha(ch)&&ch!='_') return false;
|
}
|
}
|
else {
|
else {
|
if(!std::isalnum(ch)&&ch!='_') return false;
|
if(!std::isalnum(ch)&&ch!='_') return false;
|
}
|
}
|
}
|
}
|
return true;
|
return true;
|
}
|
}
|
|
|
Assembler::Integer Assembler::numericLiteral(const std::string &str) {
|
Assembler::Integer Assembler::numericLiteral(const std::string &str) {
|
std::size_t pos;
|
std::size_t pos;
|
Integer i;
|
Integer i;
|
try {
|
try {
|
i=std::stoll(str,&pos,0);
|
i=std::stoll(str,&pos,0);
|
}
|
}
|
catch(std::exception &) {
|
catch(std::exception &) {
|
throw std::runtime_error("Ill-formed numeric literal: \""+str+"\"");
|
throw std::runtime_error("Ill-formed numeric literal: \""+str+"\"");
|
}
|
}
|
if(pos<str.size()) 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;
|
typedef std::make_signed<LinkableObject::Word>::type SignedWord;
|
|
|
if(i>static_cast<Integer>(std::numeric_limits<LinkableObject::Word>::max())||
|
if(i>static_cast<Integer>(std::numeric_limits<LinkableObject::Word>::max())||
|
i<static_cast<Integer>(std::numeric_limits<SignedWord>::min()))
|
i<static_cast<Integer>(std::numeric_limits<SignedWord>::min()))
|
throw std::runtime_error("\""+str+"\": out of range");
|
throw std::runtime_error("\""+str+"\": out of range");
|
|
|
return i;
|
return i;
|
}
|
}
|
|
|
std::vector<Assembler::Operand> Assembler::getOperands(const TokenList &list) {
|
std::vector<Assembler::Operand> Assembler::getOperands(const TokenList &list) {
|
std::vector<Operand> arglist;
|
std::vector<Operand> arglist;
|
for(std::size_t i=1;i<list.size();i++) {
|
for(std::size_t i=1;i<list.size();i++) {
|
if(i%2!=0) {
|
if(i%2!=0) {
|
Operand a;
|
Operand a;
|
a.str=list[i];
|
a.str=list[i];
|
|
|
if(!list[i].empty()&&list[i][0]=='r') {
|
if(!list[i].empty()&&list[i][0]=='r') {
|
// Is argument a register?
|
// Is argument a register?
|
char *endptr;
|
char *endptr;
|
auto regstr=list[i].substr(1);
|
auto regstr=list[i].substr(1);
|
auto reg=std::strtol(regstr.c_str(),&endptr,10);
|
auto reg=std::strtol(regstr.c_str(),&endptr,10);
|
|
|
if(!*endptr&®>=0&®<=255) {
|
if(!*endptr&®>=0&®<=255) {
|
a.type=Operand::Register;
|
a.type=Operand::Register;
|
a.reg=static_cast<std::uint8_t>(reg);
|
a.reg=static_cast<std::uint8_t>(reg);
|
arglist.push_back(std::move(a));
|
arglist.push_back(std::move(a));
|
continue;
|
continue;
|
}
|
}
|
}
|
}
|
|
|
// Try alternative register names
|
// Try alternative register names
|
if(list[i]=="sp") { // stack pointer
|
if(list[i]=="sp") { // stack pointer
|
a.type=Operand::Register;
|
a.type=Operand::Register;
|
a.reg=255;
|
a.reg=255;
|
arglist.push_back(std::move(a));
|
arglist.push_back(std::move(a));
|
}
|
}
|
else if(list[i]=="rp") { // return pointer
|
else if(list[i]=="rp") { // return pointer
|
a.type=Operand::Register;
|
a.type=Operand::Register;
|
a.reg=254;
|
a.reg=254;
|
arglist.push_back(std::move(a));
|
arglist.push_back(std::move(a));
|
}
|
}
|
else if(list[i]=="irp") { // interrupt return pointer
|
else if(list[i]=="irp") { // interrupt return pointer
|
a.type=Operand::Register;
|
a.type=Operand::Register;
|
a.reg=253;
|
a.reg=253;
|
arglist.push_back(std::move(a));
|
arglist.push_back(std::move(a));
|
}
|
}
|
else if(list[i]=="cr") { // control register
|
else if(list[i]=="cr") { // control register
|
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"&&
|
list[i][2]>='0'&&list[i][2]<='7') // 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));
|
}
|
}
|
else if(validateIdentifier(list[i])) {
|
else if(validateIdentifier(list[i])) {
|
// Is argument an identifier?
|
// Is argument an identifier?
|
a.type=Operand::Identifier;
|
a.type=Operand::Identifier;
|
arglist.push_back(std::move(a));
|
arglist.push_back(std::move(a));
|
}
|
}
|
else {
|
else {
|
auto atpos=list[i].find_first_of('@');
|
auto atpos=list[i].find_first_of('@');
|
if(atpos!=std::string::npos) {
|
if(atpos!=std::string::npos) {
|
// Identifier with an offset?
|
// Identifier with an offset?
|
a.type=Operand::Identifier;
|
a.type=Operand::Identifier;
|
a.str=list[i].substr(0,atpos);
|
a.str=list[i].substr(0,atpos);
|
if(!validateIdentifier(a.str)) throw std::runtime_error("Ill-formed identifier");
|
if(!validateIdentifier(a.str)) throw std::runtime_error("Ill-formed identifier");
|
a.i=numericLiteral(list[i].substr(atpos+1));
|
a.i=numericLiteral(list[i].substr(atpos+1));
|
arglist.push_back(std::move(a));
|
arglist.push_back(std::move(a));
|
}
|
}
|
else {
|
else {
|
// Numeric literal?
|
// Numeric literal?
|
a.type=Operand::NumericLiteral;
|
a.type=Operand::NumericLiteral;
|
a.i=numericLiteral(list[i]);
|
a.i=numericLiteral(list[i]);
|
arglist.push_back(std::move(a));
|
arglist.push_back(std::move(a));
|
}
|
}
|
}
|
}
|
}
|
}
|
else {
|
else {
|
if(list[i]!=",") throw std::runtime_error("Comma expected");
|
if(list[i]!=",") throw std::runtime_error("Comma expected");
|
if(i+1==list.size()) throw std::runtime_error("Unexpected end of line");
|
if(i+1==list.size()) throw std::runtime_error("Unexpected end of line");
|
}
|
}
|
}
|
}
|
return arglist;
|
return arglist;
|
}
|
}
|
|
|
/*
|
/*
|
* Member functions to encode LXP32 instructions
|
* Member functions to encode LXP32 instructions
|
*/
|
*/
|
|
|
void Assembler::encodeDstOperand(LinkableObject::Word &word,const Operand &arg) {
|
void Assembler::encodeDstOperand(LinkableObject::Word &word,const Operand &arg) {
|
if(arg.type!=Operand::Register)
|
if(arg.type!=Operand::Register)
|
throw std::runtime_error("\""+arg.str+"\": must be a register");
|
throw std::runtime_error("\""+arg.str+"\": must be a register");
|
word|=arg.reg<<16;
|
word|=arg.reg<<16;
|
}
|
}
|
|
|
void Assembler::encodeRd1Operand(LinkableObject::Word &word,const Operand &arg) {
|
void Assembler::encodeRd1Operand(LinkableObject::Word &word,const Operand &arg) {
|
if(arg.type==Operand::Register) {
|
if(arg.type==Operand::Register) {
|
word|=0x02000000;
|
word|=0x02000000;
|
word|=arg.reg<<8;
|
word|=arg.reg<<8;
|
}
|
}
|
else if(arg.type==Operand::NumericLiteral) {
|
else if(arg.type==Operand::NumericLiteral) {
|
if((arg.i<-128||arg.i>127)&&(arg.i<0xFFFFFF80||arg.i>0xFFFFFFFF))
|
if((arg.i<-128||arg.i>127)&&(arg.i<0xFFFFFF80||arg.i>0xFFFFFFFF))
|
throw std::runtime_error("\""+arg.str+"\": out of range");
|
throw std::runtime_error("\""+arg.str+"\": out of range");
|
auto b=static_cast<LinkableObject::Byte>(arg.i);
|
auto b=static_cast<LinkableObject::Byte>(arg.i);
|
word|=b<<8;
|
word|=b<<8;
|
}
|
}
|
else throw std::runtime_error("\""+arg.str+"\": bad argument");
|
else throw std::runtime_error("\""+arg.str+"\": bad argument");
|
}
|
}
|
|
|
void Assembler::encodeRd2Operand(LinkableObject::Word &word,const Operand &arg) {
|
void Assembler::encodeRd2Operand(LinkableObject::Word &word,const Operand &arg) {
|
if(arg.type==Operand::Register) {
|
if(arg.type==Operand::Register) {
|
word|=0x01000000;
|
word|=0x01000000;
|
word|=arg.reg;
|
word|=arg.reg;
|
}
|
}
|
else if(arg.type==Operand::NumericLiteral) {
|
else if(arg.type==Operand::NumericLiteral) {
|
if((arg.i<-128||arg.i>127)&&(arg.i<0xFFFFFF80||arg.i>0xFFFFFFFF))
|
if((arg.i<-128||arg.i>127)&&(arg.i<0xFFFFFF80||arg.i>0xFFFFFFFF))
|
throw std::runtime_error("\""+arg.str+"\": out of range");
|
throw std::runtime_error("\""+arg.str+"\": out of range");
|
auto b=static_cast<LinkableObject::Byte>(arg.i);
|
auto b=static_cast<LinkableObject::Byte>(arg.i);
|
word|=b;
|
word|=b;
|
}
|
}
|
else throw std::runtime_error("\""+arg.str+"\": bad argument");
|
else throw std::runtime_error("\""+arg.str+"\": bad argument");
|
}
|
}
|
|
|
void Assembler::encodeAdd(const TokenList &list) {
|
void Assembler::encodeAdd(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("add instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("add instruction requires 3 operands");
|
LinkableObject::Word w=0x40000000;
|
LinkableObject::Word w=0x40000000;
|
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::encodeAnd(const TokenList &list) {
|
void Assembler::encodeAnd(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("and instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("and instruction requires 3 operands");
|
LinkableObject::Word w=0x60000000;
|
LinkableObject::Word w=0x60000000;
|
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::encodeCall(const TokenList &list) {
|
void Assembler::encodeCall(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=1) throw std::runtime_error("call instruction requires 1 operand");
|
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");
|
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register");
|
LinkableObject::Word w=0x86FE0000;
|
LinkableObject::Word w=0x86FE0000;
|
encodeRd1Operand(w,args[0]);
|
encodeRd1Operand(w,args[0]);
|
_obj.addWord(w);
|
_obj.addWord(w);
|
}
|
}
|
|
|
void Assembler::encodeCjmpxx(const TokenList &list) {
|
void Assembler::encodeCjmpxx(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("cjmpxx instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("cjmpxx instruction requires 3 operands");
|
|
|
LinkableObject::Word w;
|
LinkableObject::Word w;
|
bool reverse=false;
|
bool reverse=false;
|
/*
|
/*
|
* Note: cjmpul, cjmpule, cjmpsl and cjmpsle don't have distinct opcodes;
|
* Note: cjmpul, cjmpule, cjmpsl and cjmpsle don't have distinct opcodes;
|
* instead, they are aliases for respective "g" or "ge" instructions
|
* instead, they are aliases for respective "g" or "ge" instructions
|
* with reversed operand order.
|
* with reversed operand order.
|
*/
|
*/
|
if(list[0]=="cjmpe") w=0xE0000000;
|
if(list[0]=="cjmpe") w=0xE0000000;
|
else if(list[0]=="cjmpne") w=0xD0000000;
|
else if(list[0]=="cjmpne") w=0xD0000000;
|
else if(list[0]=="cjmpug"||list[0]=="cjmpul") w=0xC8000000;
|
else if(list[0]=="cjmpug"||list[0]=="cjmpul") w=0xC8000000;
|
else if(list[0]=="cjmpuge"||list[0]=="cjmpule") w=0xE8000000;
|
else if(list[0]=="cjmpuge"||list[0]=="cjmpule") w=0xE8000000;
|
else if(list[0]=="cjmpsg"||list[0]=="cjmpsl") w=0xC4000000;
|
else if(list[0]=="cjmpsg"||list[0]=="cjmpsl") w=0xC4000000;
|
else if(list[0]=="cjmpsge"||list[0]=="cjmpsle") w=0xE4000000;
|
else if(list[0]=="cjmpsge"||list[0]=="cjmpsle") w=0xE4000000;
|
else throw std::runtime_error("Unrecognized instruction: \""+list[0]+"\"");
|
else throw std::runtime_error("Unrecognized instruction: \""+list[0]+"\"");
|
|
|
if(list[0]=="cjmpul"||list[0]=="cjmpule"||
|
if(list[0]=="cjmpul"||list[0]=="cjmpule"||
|
list[0]=="cjmpsl"||list[0]=="cjmpsle") reverse=true;
|
list[0]=="cjmpsl"||list[0]=="cjmpsle") reverse=true;
|
|
|
encodeDstOperand(w,args[0]);
|
encodeDstOperand(w,args[0]);
|
|
|
if(!reverse) {
|
if(!reverse) {
|
encodeRd1Operand(w,args[1]);
|
encodeRd1Operand(w,args[1]);
|
encodeRd2Operand(w,args[2]);
|
encodeRd2Operand(w,args[2]);
|
}
|
}
|
else {
|
else {
|
encodeRd1Operand(w,args[2]);
|
encodeRd1Operand(w,args[2]);
|
encodeRd2Operand(w,args[1]);
|
encodeRd2Operand(w,args[1]);
|
}
|
}
|
_obj.addWord(w);
|
_obj.addWord(w);
|
}
|
}
|
|
|
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");
|
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");
|
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);
|
}
|
}
|
|
|
void Assembler::encodeHlt(const TokenList &list) {
|
void Assembler::encodeHlt(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(!args.empty()) throw std::runtime_error("hlt instruction doesn't take operands");
|
if(!args.empty()) throw std::runtime_error("hlt instruction doesn't take operands");
|
_obj.addWord(0x08000000);
|
_obj.addWord(0x08000000);
|
}
|
}
|
|
|
void Assembler::encodeJmp(const TokenList &list) {
|
void Assembler::encodeJmp(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=1) throw std::runtime_error("jmp instruction requires 1 operand");
|
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");
|
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register");
|
LinkableObject::Word w=0x82000000;
|
LinkableObject::Word w=0x82000000;
|
encodeRd1Operand(w,args[0]);
|
encodeRd1Operand(w,args[0]);
|
_obj.addWord(w);
|
_obj.addWord(w);
|
}
|
}
|
|
|
void Assembler::encodeIret(const TokenList &list) {
|
void Assembler::encodeIret(const TokenList &list) {
|
// Note: "iret" is not a real instruction, but an alias for "jmp irp"
|
// Note: "iret" is not a real instruction, but an alias for "jmp irp"
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(!args.empty()) throw std::runtime_error("iret instruction doesn't take operands");
|
if(!args.empty()) throw std::runtime_error("iret instruction doesn't take operands");
|
_obj.addWord(0x8200FD00);
|
_obj.addWord(0x8200FD00);
|
}
|
}
|
|
|
void Assembler::encodeLc(const TokenList &list) {
|
void Assembler::encodeLc(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=2) throw std::runtime_error("lc instruction requires 2 operands");
|
if(args.size()!=2) throw std::runtime_error("lc instruction requires 2 operands");
|
|
|
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) {
|
LinkableObject::Reference ref;
|
LinkableObject::Reference ref;
|
ref.source=currentFileName();
|
ref.source=currentFileName();
|
ref.line=line();
|
ref.line=line();
|
ref.rva=_obj.addWord(0);
|
ref.rva=_obj.addWord(0);
|
ref.offset=args[1].i;
|
ref.offset=args[1].i;
|
ref.type=LinkableObject::Regular;
|
ref.type=LinkableObject::Regular;
|
_obj.addReference(args[1].str,ref);
|
_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) {
|
void Assembler::encodeLcs(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=2) throw std::runtime_error("lcs instruction requires 2 operands");
|
if(args.size()!=2) throw std::runtime_error("lcs instruction requires 2 operands");
|
|
|
LinkableObject::Word w=0xA0000000;
|
LinkableObject::Word w=0xA0000000;
|
encodeDstOperand(w,args[0]);
|
encodeDstOperand(w,args[0]);
|
|
|
if(args[1].type==Operand::NumericLiteral) {
|
if(args[1].type==Operand::NumericLiteral) {
|
if((args[1].i<-1048576||args[1].i>1048575)&&(args[1].i<0xFFF00000||args[1].i>0xFFFFFFFF))
|
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");
|
throw std::runtime_error("\""+args[1].str+"\": out of range");
|
auto c=static_cast<LinkableObject::Word>(args[1].i)&0x1FFFFF;
|
auto c=static_cast<LinkableObject::Word>(args[1].i)&0x1FFFFF;
|
w|=(c&0xFFFF);
|
w|=(c&0xFFFF);
|
w|=((c<<8)&0x1F000000);
|
w|=((c<<8)&0x1F000000);
|
_obj.addWord(w);
|
_obj.addWord(w);
|
}
|
}
|
else if(args[1].type==Operand::Identifier) {
|
else if(args[1].type==Operand::Identifier) {
|
LinkableObject::Reference ref;
|
LinkableObject::Reference ref;
|
ref.source=currentFileName();
|
ref.source=currentFileName();
|
ref.line=line();
|
ref.line=line();
|
ref.rva=_obj.addWord(w);
|
ref.rva=_obj.addWord(w);
|
ref.offset=args[1].i;
|
ref.offset=args[1].i;
|
ref.type=LinkableObject::Short;
|
ref.type=LinkableObject::Short;
|
_obj.addReference(args[1].str,ref);
|
_obj.addReference(args[1].str,ref);
|
}
|
}
|
else throw std::runtime_error("\""+args[1].str+"\": bad argument");
|
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;
|
encodeDstOperand(w,args[0]);
|
encodeDstOperand(w,args[0]);
|
encodeRd1Operand(w,args[1]);
|
encodeRd1Operand(w,args[1]);
|
_obj.addWord(w);
|
_obj.addWord(w);
|
}
|
}
|
|
|
void Assembler::encodeLub(const TokenList &list) {
|
void Assembler::encodeLub(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=2) throw std::runtime_error("lub instruction requires 2 operands");
|
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");
|
if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register");
|
LinkableObject::Word w=0x2A000000;
|
LinkableObject::Word w=0x2A000000;
|
encodeDstOperand(w,args[0]);
|
encodeDstOperand(w,args[0]);
|
encodeRd1Operand(w,args[1]);
|
encodeRd1Operand(w,args[1]);
|
_obj.addWord(w);
|
_obj.addWord(w);
|
}
|
}
|
|
|
void Assembler::encodeLw(const TokenList &list) {
|
void Assembler::encodeLw(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=2) throw std::runtime_error("lw instruction requires 2 operands");
|
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");
|
if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register");
|
LinkableObject::Word w=0x22000000;
|
LinkableObject::Word w=0x22000000;
|
encodeDstOperand(w,args[0]);
|
encodeDstOperand(w,args[0]);
|
encodeRd1Operand(w,args[1]);
|
encodeRd1Operand(w,args[1]);
|
_obj.addWord(w);
|
_obj.addWord(w);
|
}
|
}
|
|
|
void Assembler::encodeMods(const TokenList &list) {
|
void Assembler::encodeMods(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("mods instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("mods instruction requires 3 operands");
|
LinkableObject::Word w=0x5C000000;
|
LinkableObject::Word w=0x5C000000;
|
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::encodeModu(const TokenList &list) {
|
void Assembler::encodeModu(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("modu instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("modu instruction requires 3 operands");
|
LinkableObject::Word w=0x58000000;
|
LinkableObject::Word w=0x58000000;
|
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::encodeMov(const TokenList &list) {
|
void Assembler::encodeMov(const TokenList &list) {
|
// Note: "mov" is not a real instruction, but an alias for "add dst, src, 0"
|
// Note: "mov" is not a real instruction, but an alias for "add dst, src, 0"
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=2) throw std::runtime_error("mov instruction requires 2 operands");
|
if(args.size()!=2) throw std::runtime_error("mov instruction requires 2 operands");
|
LinkableObject::Word w=0x40000000;
|
LinkableObject::Word w=0x40000000;
|
encodeDstOperand(w,args[0]);
|
encodeDstOperand(w,args[0]);
|
encodeRd1Operand(w,args[1]);
|
encodeRd1Operand(w,args[1]);
|
_obj.addWord(w);
|
_obj.addWord(w);
|
}
|
}
|
|
|
void Assembler::encodeMul(const TokenList &list) {
|
void Assembler::encodeMul(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("mul instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("mul instruction requires 3 operands");
|
LinkableObject::Word w=0x48000000;
|
LinkableObject::Word w=0x48000000;
|
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::encodeNeg(const TokenList &list) {
|
void Assembler::encodeNeg(const TokenList &list) {
|
// Note: "neg" is not a real instruction, but an alias for "sub dst, 0, src"
|
// Note: "neg" is not a real instruction, but an alias for "sub dst, 0, src"
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=2) throw std::runtime_error("neg instruction requires 2 operands");
|
if(args.size()!=2) throw std::runtime_error("neg instruction requires 2 operands");
|
LinkableObject::Word w=0x44000000;
|
LinkableObject::Word w=0x44000000;
|
encodeDstOperand(w,args[0]);
|
encodeDstOperand(w,args[0]);
|
encodeRd2Operand(w,args[1]);
|
encodeRd2Operand(w,args[1]);
|
_obj.addWord(w);
|
_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);
|
}
|
}
|
|
|
void Assembler::encodeNot(const TokenList &list) {
|
void Assembler::encodeNot(const TokenList &list) {
|
// Note: "not" is not a real instruction, but an alias for "xor dst, src, -1"
|
// Note: "not" is not a real instruction, but an alias for "xor dst, src, -1"
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=2) throw std::runtime_error("not instruction requires 2 operands");
|
if(args.size()!=2) throw std::runtime_error("not instruction requires 2 operands");
|
LinkableObject::Word w=0x680000FF;
|
LinkableObject::Word w=0x680000FF;
|
encodeDstOperand(w,args[0]);
|
encodeDstOperand(w,args[0]);
|
encodeRd1Operand(w,args[1]);
|
encodeRd1Operand(w,args[1]);
|
_obj.addWord(w);
|
_obj.addWord(w);
|
}
|
}
|
|
|
void Assembler::encodeOr(const TokenList &list) {
|
void Assembler::encodeOr(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("or instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("or instruction requires 3 operands");
|
LinkableObject::Word w=0x64000000;
|
LinkableObject::Word w=0x64000000;
|
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::encodeRet(const TokenList &list) {
|
void Assembler::encodeRet(const TokenList &list) {
|
// Note: "ret" is not a real instruction, but an alias for "jmp rp"
|
// Note: "ret" is not a real instruction, but an alias for "jmp rp"
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(!args.empty()) throw std::runtime_error("ret instruction doesn't take operands");
|
if(!args.empty()) throw std::runtime_error("ret instruction doesn't take operands");
|
_obj.addWord(0x8200FE00);
|
_obj.addWord(0x8200FE00);
|
}
|
}
|
|
|
void Assembler::encodeSb(const TokenList &list) {
|
void Assembler::encodeSb(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=2) throw std::runtime_error("sb instruction requires 2 operands");
|
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[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register");
|
if(args[1].type==Operand::NumericLiteral) {
|
if(args[1].type==Operand::NumericLiteral) {
|
// If numeric literal value is between 128 and 255 (inclusive), convert
|
// If numeric literal value is between 128 and 255 (inclusive), convert
|
// it to a signed byte to avoid exception in encodeRd2Operand()
|
// it to a signed byte to avoid exception in encodeRd2Operand()
|
if(args[1].i>=128&&args[1].i<=255) args[1].i-=256;
|
if(args[1].i>=128&&args[1].i<=255) args[1].i-=256;
|
}
|
}
|
LinkableObject::Word w=0x3A000000;
|
LinkableObject::Word w=0x3A000000;
|
encodeRd1Operand(w,args[0]);
|
encodeRd1Operand(w,args[0]);
|
encodeRd2Operand(w,args[1]);
|
encodeRd2Operand(w,args[1]);
|
_obj.addWord(w);
|
_obj.addWord(w);
|
}
|
}
|
|
|
void Assembler::encodeSl(const TokenList &list) {
|
void Assembler::encodeSl(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("sl instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("sl instruction requires 3 operands");
|
if(args[2].type==Operand::NumericLiteral&&
|
if(args[2].type==Operand::NumericLiteral&&
|
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word))))
|
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word))))
|
{
|
{
|
std::cerr<<currentFileName()<<":"<<line()<<": ";
|
std::cerr<<currentFileName()<<":"<<line()<<": ";
|
std::cerr<<"Warning: Bitwise shift result is undefined when "
|
std::cerr<<"Warning: Bitwise shift result is undefined when "
|
"the second operand is negative or greater than 31"<<std::endl;
|
"the second operand is negative or greater than 31"<<std::endl;
|
}
|
}
|
|
|
LinkableObject::Word w=0x70000000;
|
LinkableObject::Word w=0x70000000;
|
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::encodeSrs(const TokenList &list) {
|
void Assembler::encodeSrs(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("srs instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("srs instruction requires 3 operands");
|
if(args[2].type==Operand::NumericLiteral&&
|
if(args[2].type==Operand::NumericLiteral&&
|
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word))))
|
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word))))
|
{
|
{
|
std::cerr<<currentFileName()<<":"<<line()<<": ";
|
std::cerr<<currentFileName()<<":"<<line()<<": ";
|
std::cerr<<"Warning: Bitwise shift result is undefined when "
|
std::cerr<<"Warning: Bitwise shift result is undefined when "
|
"the second operand is negative or greater than 31"<<std::endl;
|
"the second operand is negative or greater than 31"<<std::endl;
|
}
|
}
|
|
|
LinkableObject::Word w=0x7C000000;
|
LinkableObject::Word w=0x7C000000;
|
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::encodeSru(const TokenList &list) {
|
void Assembler::encodeSru(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("sru instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("sru instruction requires 3 operands");
|
if(args[2].type==Operand::NumericLiteral&&
|
if(args[2].type==Operand::NumericLiteral&&
|
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word))))
|
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word))))
|
{
|
{
|
std::cerr<<currentFileName()<<":"<<line()<<": ";
|
std::cerr<<currentFileName()<<":"<<line()<<": ";
|
std::cerr<<"Warning: Bitwise shift result is undefined when "
|
std::cerr<<"Warning: Bitwise shift result is undefined when "
|
"the second operand is negative or greater than 31"<<std::endl;
|
"the second operand is negative or greater than 31"<<std::endl;
|
}
|
}
|
|
|
LinkableObject::Word w=0x78000000;
|
LinkableObject::Word w=0x78000000;
|
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::encodeSub(const TokenList &list) {
|
void Assembler::encodeSub(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("sub instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("sub instruction requires 3 operands");
|
LinkableObject::Word w=0x44000000;
|
LinkableObject::Word w=0x44000000;
|
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::encodeSw(const TokenList &list) {
|
void Assembler::encodeSw(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=2) throw std::runtime_error("sw instruction requires 2 operands");
|
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");
|
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register");
|
LinkableObject::Word w=0x32000000;
|
LinkableObject::Word w=0x32000000;
|
encodeRd1Operand(w,args[0]);
|
encodeRd1Operand(w,args[0]);
|
encodeRd2Operand(w,args[1]);
|
encodeRd2Operand(w,args[1]);
|
_obj.addWord(w);
|
_obj.addWord(w);
|
}
|
}
|
|
|
void Assembler::encodeXor(const TokenList &list) {
|
void Assembler::encodeXor(const TokenList &list) {
|
auto args=getOperands(list);
|
auto args=getOperands(list);
|
if(args.size()!=3) throw std::runtime_error("xor instruction requires 3 operands");
|
if(args.size()!=3) throw std::runtime_error("xor instruction requires 3 operands");
|
LinkableObject::Word w=0x68000000;
|
LinkableObject::Word w=0x68000000;
|
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);
|
}
|
}
|
|
|