summaryrefslogtreecommitdiffstats
path: root/microbe/microbe.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'microbe/microbe.cpp')
-rw-r--r--microbe/microbe.cpp472
1 files changed, 472 insertions, 0 deletions
diff --git a/microbe/microbe.cpp b/microbe/microbe.cpp
new file mode 100644
index 0000000..d94cba7
--- /dev/null
+++ b/microbe/microbe.cpp
@@ -0,0 +1,472 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke [email protected] *
+ * Copyright (C) 2005 by David Saxton *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "instruction.h"
+#include "microbe.h"
+#include "parser.h"
+#include "optimizer.h"
+#include "pic14.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <qfile.h>
+
+#include <iostream>
+using namespace std;
+
+
+//BEGIN class Microbe
+Microbe::Microbe()
+{
+ m_maxDelaySubroutine = PIC14::Delay_None;
+ m_dest = 0;
+ m_uniqueLabel = 0;
+
+ // Hardwired constants
+ m_aliasList["true"] = "1";
+ m_aliasList["false"] = "0";
+ // Things starting with b are reserved by gpasm (for binary numbers)
+ m_aliasList["b"] = "_b";
+
+ //BEGIN Keypad values
+ int bv[4][6] = {
+ { 1, 2, 3, 10, 14, 18 },
+ { 4, 5, 6, 11, 15, 19 },
+ { 7, 8, 9, 12, 16, 20 },
+ { 253, 0, 254, 13, 17, 21 } };
+
+ for ( unsigned row = 0; row < 4; ++row )
+ {
+ for ( unsigned col = 0; col < 6; ++col )
+ {
+ m_aliasList[ QString("Keypad_%1_%2").arg(row+1).arg(col+1) ] = QString::number( bv[row][col] );
+ }
+ }
+
+ m_aliasList[ "Keypad_None" ] = "0xff";
+ //END Keypad values
+}
+
+
+Microbe::~Microbe()
+{
+}
+
+
+QString Microbe::compile( const QString & url, bool showSource, bool optimize )
+{
+ QFile file( url );
+ if( file.open( IO_ReadOnly ) )
+ {
+ QTextStream stream(&file);
+ unsigned line = 0;
+ while( !stream.atEnd() )
+ m_program += SourceLine( stream.readLine(), url, line++ );
+ file.close();
+ simplifyProgram();
+ }
+ else
+ {
+ m_errorReport += i18n("Could not open file '%1'\n").arg(url);
+ return 0;
+ }
+
+ Parser parser(this);
+
+ // Extract the PIC ID
+ if ( !m_program.isEmpty() )
+ {
+ m_picType = PIC14::toType( m_program[0].text() );
+ m_program.remove( m_program.begin() );
+ }
+
+ PIC14 * pic = makePic();
+ if ( !pic )
+ return 0;
+
+ Code * code = parser.parse( m_program );
+ pic->setCode( code );
+ pic->addCommonFunctions( (PIC14::DelaySubroutine)m_maxDelaySubroutine );
+
+ pic->postCompileConstruct( m_usedInterrupts );
+ code->postCompileConstruct();
+
+ if ( optimize )
+ {
+ Optimizer opt;
+ opt.optimize( code );
+ }
+
+ return code->generateCode( pic );
+}
+
+
+PIC14 * Microbe::makePic()
+{
+ return new PIC14( this, (PIC14::Type)m_picType );
+}
+
+
+void Microbe::simplifyProgram()
+{
+ SourceLineList simplified;
+
+ enum CommentType { None, SingleLine, MultiLine };
+ CommentType commentType = None;
+
+ SourceLineList::const_iterator end = m_program.end();
+ for ( SourceLineList::const_iterator it = m_program.begin(); it != end; ++it )
+ {
+ QString code = (*it).text();
+ QString simplifiedLine;
+
+ if ( commentType == SingleLine )
+ commentType = None;
+
+ unsigned l = code.length();
+
+ for ( unsigned i = 0; i < l; ++i )
+ {
+ QChar c = code[i];
+ switch ( c )
+ {
+ case '/':
+ // Look for start of comments in form "//" and "/*"
+
+ if ( commentType == None && (i+1 < l) )
+ {
+ if ( code[i+1] == '/' )
+ {
+ commentType = SingleLine;
+ i++;
+ }
+
+ else if ( code[i+1] == '*' )
+ {
+ commentType = MultiLine;
+ i++;
+ }
+ }
+ break;
+
+ case '*':
+ // Look for end of comments in form "*/"
+ if ( commentType == MultiLine && (i+1 < l) && code[i+1] == '/' )
+ {
+ i++;
+ commentType = None;
+ continue;
+ }
+ break;
+
+ case '{':
+ case '}':
+ // Put braces on seperate lines
+
+ if ( commentType != None )
+ break;
+
+ simplified << SourceLine( simplifiedLine.simplifyWhiteSpace(), (*it).url(), (*it).line() );
+ simplified << SourceLine( c, (*it).url(), (*it).line() );
+
+ simplifiedLine = "";
+ continue;
+ }
+
+ if ( commentType == None )
+ simplifiedLine += c;
+ }
+
+ simplified << SourceLine( simplifiedLine.simplifyWhiteSpace(), (*it).url(), (*it).line() );
+ }
+
+ m_program.clear();
+ end = simplified.end();
+ for ( SourceLineList::const_iterator it = simplified.begin(); it != end; ++it )
+ {
+ if ( !(*it).text().isEmpty() )
+ m_program << *it;
+ }
+}
+
+
+void Microbe::compileError( MistakeType type, const QString & context, const SourceLine & sourceLine )
+{
+ QString message;
+ switch (type)
+ {
+ case UnknownStatement:
+ message = i18n("Unknown statement");
+ break;
+ case InvalidPort:
+ message = i18n("Port '%1' is not supported by target PIC").arg(context);
+ break;
+ case UnassignedPin:
+ message = i18n("Pin identifier was not followed by '='");
+ break;
+ case NonHighLowPinState:
+ message = i18n("Pin state can only be 'high' or 'low'");
+ break;
+ case UnassignedPort:
+ message = i18n("Invalid token '%1'. Port identifier should be followed by '='").arg(context);
+ break;
+ case UnexpectedStatementBeforeBracket:
+ message = i18n("Unexpected statement before '{'");
+ break;
+ case MismatchedBrackets:
+ message = i18n("Mismatched brackets in expression '%1'").arg(context);
+ break;
+ case InvalidEquals:
+ message = i18n("Invalid '=' found in expression");
+ break;
+ case ReservedKeyword:
+ message = i18n("Reserved keyword '%1' cannot be a variable name.").arg(context);
+ break;
+ case ConsecutiveOperators:
+ message = i18n("Nothing between operators");
+ break;
+ case MissingOperator:
+ message = i18n("Missing operator or space in operand");
+ break;
+ case UnknownVariable:
+ if ( context.isEmpty() )
+ message = i18n("Unknown variable");
+ else
+ message = i18n("Unknown variable '%1'").arg(context);
+ break;
+ case UnopenableInclude:
+ message = i18n("Could not open include file '%1'").arg(context);
+ break;
+ case DivisionByZero:
+ message = i18n("Division by zero");
+ break;
+ case NumberTooBig:
+ message = i18n("Number too big");
+ break;
+ case NonConstantStep:
+ message = i18n("Step can only be a constant expression");
+ break;
+ case NonConstantDelay:
+ message = i18n("Delay must be a positive constant value");
+ break;
+ case HighLowExpected:
+ message = i18n("'high' or 'low' expected after pin expression '%1'").arg(context);
+ break;
+ case InvalidComparison:
+ message = i18n("Comparison operator in '%1' is not recognized");
+ break;
+ case SubBeforeEnd:
+ message = i18n("Subroutine definition before end of program");
+ break;
+ case InterruptBeforeEnd:
+ message = i18n("Interrupt routine definition before end of program");
+ break;
+ case LabelExpected:
+ message = i18n("Label expected");
+ break;
+ case TooManyTokens:
+ message = i18n("Extra tokens at end of line");
+ break;
+ case FixedStringExpected:
+ message = i18n("Expected '%1'").arg(context);
+ break;
+ case PinListExpected:
+ message = i18n("Pin list expected");
+ break;
+ case AliasRedefined:
+ message = i18n("Alias already definied");
+ break;
+ case InvalidInterrupt:
+ message = i18n("Interrupt type not supported by target PIC");
+ break;
+ case InterruptRedefined:
+ message = i18n("Interrupt already definied");
+ break;
+ case ReadOnlyVariable:
+ message = i18n("Variable '%1' is read only").arg(context);
+ break;
+ case WriteOnlyVariable:
+ message = i18n("Variable '%1' is write only").arg(context);
+ break;
+ case InvalidPinMapSize:
+ message = i18n("Invalid pin list size");
+ break;
+ case VariableRedefined:
+ message = i18n("Variable '%1' is already defined").arg(context);
+ break;
+ case InvalidVariableName:
+ message = i18n("'%1' is not a valid variable name").arg(context);
+ break;
+ case VariableExpected:
+ message = i18n("Variable expected");
+ break;
+ case NameExpected:
+ message = i18n("Name expected");
+ break;
+ }
+
+
+ m_errorReport += QString("%1:%2:Error [%3] %4\n")
+ .arg( sourceLine.url() )
+ .arg( sourceLine.line()+1 )
+ .arg( type )
+ .arg( message );
+}
+
+
+bool Microbe::isValidVariableName( const QString & variableName )
+{
+ if ( variableName.isEmpty() )
+ return false;
+
+ if ( !variableName[0].isLetter() && variableName[0] != '_' )
+ return false;
+
+ for ( unsigned i = 1; i < variableName.length(); ++i )
+ {
+ if ( !variableName[i].isLetterOrNumber() && variableName[i] != '_' )
+ return false;
+ }
+
+ return true;
+}
+
+
+void Microbe::addVariable( const Variable & variable )
+{
+ if ( variable.type() == Variable::invalidType )
+ return;
+
+ if ( !isVariableKnown( variable.name() ) )
+ m_variables << variable;
+}
+
+
+Variable Microbe::variable( const QString & name ) const
+{
+ VariableList::const_iterator end = m_variables.end();
+ for ( VariableList::const_iterator it = m_variables.begin(); it != end; ++it )
+ {
+ if ( (*it).name() == name )
+ return *it;
+ }
+ return Variable();
+}
+
+
+bool Microbe::isVariableKnown( const QString & name ) const
+{
+ return variable(name).type() != Variable::invalidType;
+}
+
+
+void Microbe::addDelayRoutineWanted( unsigned routine )
+{
+ if ( m_maxDelaySubroutine < routine )
+ m_maxDelaySubroutine = routine;
+}
+
+
+void Microbe::addAlias( const QString & name, const QString & dest )
+{
+ m_aliasList[name] = dest;
+}
+
+
+QString Microbe::alias( const QString & alias ) const
+{
+ // If the string is an alias, return the real string,
+ // otherwise just return the alias as that is the real string.
+ AliasMap::const_iterator it = m_aliasList.find(alias);
+ if ( it != m_aliasList.constEnd() )
+ return it.data();
+ return alias;
+}
+
+
+void Microbe::setInterruptUsed(const QString &interruptName)
+{
+ // Don't add it again if it is already in the list
+ if ( m_usedInterrupts.contains( interruptName ) )
+ return;
+ m_usedInterrupts.append(interruptName);
+}
+
+
+bool Microbe::isInterruptUsed( const QString & interruptName )
+{
+ return m_usedInterrupts.contains( interruptName );
+}
+
+
+QString Microbe::dest() const
+{
+ return QString("__op%1").arg(m_dest);
+}
+
+
+void Microbe::incDest()
+{
+ m_dest++;
+// if ( ++m_dest > m_highestDest )
+// m_highestDest = m_dest;
+}
+
+
+void Microbe::decDest()
+{
+ m_dest--;
+}
+
+
+void Microbe::resetDest()
+{
+ m_dest = 0;
+}
+//END class Microbe
+
+
+
+//BEGIN class SourceLine
+SourceLine::SourceLine( const QString & text, const QString & url, int line )
+{
+ m_text = text;
+ m_url = url;
+ m_line = line;
+}
+
+
+SourceLine::SourceLine()
+{
+ m_line = -1;
+}
+
+
+QStringList SourceLine::toStringList( const SourceLineList & lines )
+{
+ QStringList joined;
+ SourceLineList::const_iterator end = lines.end();
+ for ( SourceLineList::const_iterator it = lines.begin(); it != end; ++it )
+ joined << (*it).text();
+ return joined;
+
+}
+//END class SourceLine
+