|
|
|
/***************************************************************************
|
|
|
|
* Copyright (C) 2004-2005 by Daniel Clarke daniel.jc@gmail.com *
|
|
|
|
* 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 <tdelocale.h>
|
|
|
|
#include <tqfile.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[ TQString("Keypad_%1_%2").arg(row+1).arg(col+1) ] = TQString::number( bv[row][col] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_aliasList[ "Keypad_None" ] = "0xff";
|
|
|
|
//END Keypad values
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Microbe::~Microbe()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQString Microbe::compile( const TQString & url, bool showSource, bool optimize )
|
|
|
|
{
|
|
|
|
TQFile file( url );
|
|
|
|
if( file.open( IO_ReadOnly ) )
|
|
|
|
{
|
|
|
|
TQTextStream 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 )
|
|
|
|
{
|
|
|
|
TQString code = (*it).text();
|
|
|
|
TQString simplifiedLine;
|
|
|
|
|
|
|
|
if ( commentType == SingleLine )
|
|
|
|
commentType = None;
|
|
|
|
|
|
|
|
unsigned l = code.length();
|
|
|
|
|
|
|
|
for ( unsigned i = 0; i < l; ++i )
|
|
|
|
{
|
|
|
|
TQChar 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 TQString & context, const SourceLine & sourceLine )
|
|
|
|
{
|
|
|
|
TQString 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 += TQString("%1:%2:Error [%3] %4\n")
|
|
|
|
.arg( sourceLine.url() )
|
|
|
|
.arg( sourceLine.line()+1 )
|
|
|
|
.arg( type )
|
|
|
|
.arg( message );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Microbe::isValidVariableName( const TQString & 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 TQString & 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 TQString & name ) const
|
|
|
|
{
|
|
|
|
return variable(name).type() != Variable::invalidType;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Microbe::addDelayRoutineWanted( unsigned routine )
|
|
|
|
{
|
|
|
|
if ( m_maxDelaySubroutine < routine )
|
|
|
|
m_maxDelaySubroutine = routine;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Microbe::addAlias( const TQString & name, const TQString & dest )
|
|
|
|
{
|
|
|
|
m_aliasList[name] = dest;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQString Microbe::alias( const TQString & 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 TQString &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 TQString & interruptName )
|
|
|
|
{
|
|
|
|
return m_usedInterrupts.contains( interruptName );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQString Microbe::dest() const
|
|
|
|
{
|
|
|
|
return TQString("__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 TQString & text, const TQString & url, int line )
|
|
|
|
{
|
|
|
|
m_text = text;
|
|
|
|
m_url = url;
|
|
|
|
m_line = line;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SourceLine::SourceLine()
|
|
|
|
{
|
|
|
|
m_line = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQStringList SourceLine::toStringList( const SourceLineList & lines )
|
|
|
|
{
|
|
|
|
TQStringList 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
|
|
|
|
|