File indexing completed on 2024-07-21 08:05:26

0001 /***************************************************************************
0002  *   Copyright (C) 2004-2005 by Daniel Clarke   daniel.jc@gmail.com        *
0003  *   Copyright (C)      2005 by David Saxton                               *
0004  *                                     *
0005  *   24-04-2007                                                            *
0006  *   Modified to add pic 16f877,16f627 and 16f628              *
0007  *   by george john george@space-kerala.org                    *
0008  *   supported by SPACE www.space-kerala.org                   *
0009  *                                                                         *
0010  *   This program is free software; you can redistribute it and/or modify  *
0011  *   it under the terms of the GNU General Public License as published by  *
0012  *   the Free Software Foundation; either version 2 of the License, or     *
0013  *   (at your option) any later version.                                   *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful,       *
0016  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0018  *   GNU General Public License for more details.                          *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program; if not, write to the                         *
0022  *   Free Software Foundation, Inc.,                                       *
0023  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
0024  ***************************************************************************/
0025 
0026 #include "instruction.h"
0027 #include "microbe.h"
0028 #include "parser.h"
0029 #include "optimizer.h"
0030 #include "pic14.h"
0031 
0032 #include <KLocalizedString>
0033 
0034 #include <QDebug>
0035 #include <QFile>
0036 
0037 #include <iostream>
0038 using namespace std;
0039 
0040 
0041 //BEGIN class MicrobeApp
0042 MicrobeApp::MicrobeApp()
0043 {
0044     m_maxDelaySubroutine = PIC14::Delay_None;
0045     m_dest = 0;
0046     m_uniqueLabel = 0;
0047 
0048     // Hardwired constants
0049     m_aliasList["true"] = "1";
0050     m_aliasList["false"] = "0";
0051     // Things starting with b are reserved by gpasm (for binary numbers)
0052     m_aliasList["b"] = "_b";
0053 
0054     //BEGIN Keypad values
0055     int bv[4][6] = {
0056         { 1,   2, 3,   10, 14, 18 },
0057         { 4,   5, 6,   11, 15, 19 },
0058         { 7,   8, 9,   12, 16, 20 },
0059         { 253, 0, 254, 13, 17, 21 } };
0060 
0061     for ( unsigned row = 0; row < 4; ++row )
0062     {
0063         for ( unsigned col = 0; col < 6; ++col )
0064         {
0065             m_aliasList[ QString("Keypad_%1_%2").arg(row+1).arg(col+1) ] = QString::number( bv[row][col] );
0066         }
0067     }
0068 
0069     m_aliasList[ "Keypad_None" ] = "0xff";
0070     //END Keypad values
0071 }
0072 
0073 
0074 MicrobeApp::~MicrobeApp()
0075 {
0076 }
0077 
0078 
0079 QString MicrobeApp::compile( const QString & url, bool optimize )
0080 {
0081     QFile file( url );
0082     if( file.open( QIODevice::ReadOnly ) )
0083     {
0084         QTextStream stream(&file);
0085         unsigned line = 0;
0086         while( !stream.atEnd() )
0087             m_program += SourceLineMicrobe( stream.readLine(), url, line++ );
0088         file.close();
0089         simplifyProgram();
0090     }
0091     else
0092     {
0093         m_errorReport += i18n("Could not open file '%1'\n", url);
0094         return nullptr;
0095     }
0096 
0097     Parser parser(this);
0098 
0099     // Extract the PIC ID
0100     if ( !m_program.isEmpty() )
0101     {
0102         m_picType = PIC14::toType( m_program[0].text() );
0103         m_program.erase( m_program.begin() );
0104     }
0105 
0106     PIC14 * pic = makePic();
0107     if ( !pic )
0108         return nullptr;
0109 
0110     Code * code = parser.parse( m_program );
0111     pic->setCode( code );
0112     pic->addCommonFunctions( static_cast<PIC14::DelaySubroutine>(m_maxDelaySubroutine) );
0113 
0114     pic->postCompileConstruct( m_usedInterrupts );
0115     code->postCompileConstruct();
0116 
0117     if ( optimize )
0118     {
0119         Optimizer opt;
0120         opt.optimize( code );
0121     }
0122 
0123     return code->generateCode( pic );
0124 }
0125 
0126 
0127 PIC14 * MicrobeApp::makePic()
0128 {
0129     return new PIC14( this, static_cast<PIC14::Type>(m_picType) );
0130 }
0131 
0132 
0133 void MicrobeApp::simplifyProgram()
0134 {
0135     SourceLineList simplified;
0136 
0137     enum CommentType { None, SingleLine, MultiLine };
0138     CommentType commentType = None;
0139 
0140     SourceLineList::const_iterator end = m_program.end();
0141     for ( SourceLineList::const_iterator it = m_program.begin(); it != end; ++it )
0142     {
0143         QString code = (*it).text();
0144         QString simplifiedLine;
0145 
0146         if ( commentType == SingleLine )
0147             commentType = None;
0148 
0149         unsigned l = code.length();
0150 
0151         for ( unsigned i = 0; i < l; ++i )
0152         {
0153             QChar c = code[i];
0154             const char cc = c.toLatin1();
0155             switch ( cc )
0156             {
0157                 case '/':
0158                     // Look for start of comments in form "//" and "/*"
0159 
0160                     if ( commentType == None && (i+1 < l) )
0161                     {
0162                         if ( code[i+1] == '/' )
0163                         {
0164                             commentType = SingleLine;
0165                             i++;
0166                         }
0167 
0168                         else if ( code[i+1] == '*' )
0169                         {
0170                             commentType = MultiLine;
0171                             i++;
0172                         }
0173                     }
0174                     break;
0175 
0176                 case '*':
0177                     // Look for end of comments in form "*/"
0178                     if ( commentType == MultiLine && (i+1 < l) && code[i+1] == '/' )
0179                     {
0180                         i++;
0181                         commentType = None;
0182                         continue;
0183                     }
0184                     break;
0185 
0186                 case '{':
0187                 case '}':
0188                     // Put braces on separate lines
0189 
0190                     if ( commentType != None )
0191                         break;
0192 
0193                     simplified << SourceLineMicrobe( simplifiedLine.simplified(), (*it).url(), (*it).line() );
0194                     simplified << SourceLineMicrobe( c, (*it).url(), (*it).line() );
0195 
0196                     simplifiedLine = "";
0197                     continue;
0198             }
0199 
0200             if ( commentType == None )
0201                 simplifiedLine += c;
0202         }
0203 
0204         simplified << SourceLineMicrobe( simplifiedLine.simplified(), (*it).url(), (*it).line() );
0205     }
0206 
0207     m_program.clear();
0208     end = simplified.end();
0209     for ( SourceLineList::const_iterator it = simplified.begin(); it != end; ++it )
0210     {
0211         if ( !(*it).text().isEmpty() )
0212             m_program << *it;
0213     }
0214 }
0215 
0216 
0217 void MicrobeApp::compileError( MistakeType type, const QString & context, const SourceLineMicrobe & sourceLine )
0218 {
0219     QString message;
0220     switch (type)
0221     {
0222         case UnknownStatement:
0223             message = i18n("Unknown statement");
0224             break;
0225         case InvalidPort:
0226             message = i18n("Port '%1' is not supported by target PIC", context);
0227             break;
0228         case UnassignedPin:
0229             message = i18n("Pin identifier was not followed by '='");
0230             break;
0231         case NonHighLowPinState:
0232             message = i18n("Pin state can only be 'high' or 'low'");
0233             break;
0234         case UnassignedPort:
0235             message = i18n("Invalid token '%1'. Port identifier should be followed by '='", context);
0236             break;
0237         case UnexpectedStatementBeforeBracket:
0238             message = i18n("Unexpected statement before '{'");
0239             break;
0240         case MismatchedBrackets:
0241             message = i18n("Mismatched brackets in expression '%1'", context);
0242             break;
0243         case InvalidEquals:
0244             message = i18n("Invalid '=' found in expression");
0245             break;
0246         case ReservedKeyword:
0247             message = i18n("Reserved keyword '%1' cannot be a variable name.", context);
0248             break;
0249         case ConsecutiveOperators:
0250             message = i18n("Nothing between operators");
0251             break;
0252         case MissingOperator:
0253             message = i18n("Missing operator or space in operand");
0254             break;
0255         case UnknownVariable:
0256             if ( context.isEmpty() )
0257                 message = i18n("Unknown variable");
0258             else
0259                 message = i18n("Unknown variable '%1'", context);
0260             break;
0261         case UnopenableInclude:
0262             message = i18n("Could not open include file '%1'", context);
0263             break;
0264         case DivisionByZero:
0265             message = i18n("Division by zero");
0266             break;
0267         case NumberTooBig:
0268             message = i18n("Number too big");
0269             break;
0270         case NonConstantStep:
0271             message = i18n("Step can only be a constant expression");
0272             break;
0273         case NonConstantDelay:
0274             message = i18n("Delay must be a positive constant value");
0275             break;
0276         case HighLowExpected:
0277             message = i18n("'high' or 'low' expected after pin expression '%1'", context);
0278             break;
0279         case InvalidComparison:
0280             message = i18n("Comparison operator in '%1' is not recognized");
0281             break;
0282         case SubBeforeEnd:
0283             message = i18n("Subroutine definition before end of program");
0284             break;
0285         case InterruptBeforeEnd:
0286             message = i18n("Interrupt routine definition before end of program");
0287             break;
0288         case LabelExpected:
0289             message = i18n("Label expected");
0290             break;
0291         case TooManyTokens:
0292             message = i18n("Extra tokens at end of line");
0293             break;
0294         case FixedStringExpected:
0295             message = i18n("Expected '%1'", context);
0296             break;
0297         case PinListExpected:
0298             message = i18n("Pin list expected");
0299             break;
0300         case AliasRedefined:
0301             message = i18n("Alias already defined");
0302             break;
0303         case InvalidInterrupt:
0304             message = i18n("Interrupt type not supported by target PIC");
0305             break;
0306         case InterruptRedefined:
0307             message = i18n("Interrupt already defined");
0308             break;
0309         case ReadOnlyVariable:
0310             message = i18n("Variable '%1' is read only", context);
0311             break;
0312         case WriteOnlyVariable:
0313             message = i18n("Variable '%1' is write only", context);
0314             break;
0315         case InvalidPinMapSize:
0316             message = i18n("Invalid pin list size");
0317             break;
0318         case VariableRedefined:
0319             message = i18n("Variable '%1' is already defined", context);
0320             break;
0321         case InvalidVariableName:
0322             message = i18n("'%1' is not a valid variable name", context);
0323             break;
0324         case VariableExpected:
0325             message = i18n("Variable expected");
0326             break;
0327         case NameExpected:
0328             message = i18n("Name expected");
0329             break;
0330     }
0331 
0332 
0333     m_errorReport += QString("%1:%2:Error [%3] %4\n")
0334             .arg( sourceLine.url() )
0335             .arg( sourceLine.line()+1 )
0336             .arg( type )
0337             .arg( message );
0338 }
0339 
0340 
0341 bool MicrobeApp::isValidVariableName( const QString & variableName)
0342 {
0343 
0344 //*****modified checking is included for preventing the uses of registername as varable name*****
0345 
0346 //Prevent usage of register/port name as varable
0347     if (/*PORT-NAME*/ variableName == "PORTA"
0348         || variableName == "PORTB"
0349         || variableName == "PORTC"
0350         || variableName == "PORTD"
0351         || variableName == "PORTE" /*TRIS-NAME*/
0352         || variableName == "TRISA"
0353         || variableName == "TRISB"
0354         || variableName == "TRISC"
0355         || variableName == "TRISD"
0356         || variableName == "TRISE" /**REGISTER-NAME**/
0357         || variableName == "TMR0"
0358         || variableName == "PCL"
0359         || variableName == "STATUS"
0360         || variableName == "FSR"
0361         || variableName == "PCLATH"
0362         || variableName == "INTCON"
0363         || variableName == "PIR1"
0364         || variableName == "PIR2"
0365         || variableName == "TMR1L"
0366         || variableName == "TMR1H"
0367         || variableName == "T1CON"
0368         || variableName == "TMR2"
0369         || variableName == "T2CON"
0370         || variableName == "SSPBUF"
0371         || variableName == "SSPCON"
0372         || variableName == "CCPR1L"
0373         || variableName == "CCPR1H"
0374         || variableName == "CCP1CON"
0375         || variableName == "RCSTA"
0376         || variableName == "TXREG"
0377         || variableName == "RCREG"
0378         || variableName == "CCPR2L"
0379         || variableName == "CCPR2H"
0380         || variableName == "CCP2CON"
0381         || variableName == "ADRESH"
0382         || variableName == "ADCON0" /*bank0ends*/
0383         || variableName == "OPTION_REG"
0384         || variableName == "PIE1"
0385         || variableName == "PIE2"
0386         || variableName == "PCON"
0387         || variableName == "SSPCON2"
0388         || variableName == "PR2"
0389         || variableName == "SSPADD"
0390         || variableName == "SSPSTAT"
0391         || variableName == "TXSTA"
0392         || variableName == "SPBRG"
0393         || variableName == "ADRESL"
0394         || variableName == "ADCON1" /*bank1ends*/
0395         || variableName == "EEDATA "
0396         || variableName == "EEADR"
0397         || variableName == "EEDATH"
0398         || variableName == "EEADRH" /*bank2ends*/
0399         || variableName == "EECON1"
0400         || variableName == "EECON2"  /*bank3ends*/ )
0401             return false;
0402 
0403 //****************************modification ends*******************************
0404 
0405     if ( variableName.isEmpty() )
0406         return false;
0407 
0408     if ( !variableName[0].isLetter() && variableName[0] != '_' )
0409         return false;
0410 
0411     for ( int i = 1; i < variableName.length(); ++i )
0412     {
0413         if ( !variableName[i].isLetterOrNumber() && variableName[i] != '_' )
0414             return false;
0415     }
0416 
0417     return true;
0418 }
0419 
0420 
0421 void MicrobeApp::addVariable( const Variable & variable )
0422 {
0423     if ( variable.type() == Variable::invalidType )
0424         return;
0425 
0426     if ( !isVariableKnown( variable.name() ) )
0427         m_variables << variable;
0428 }
0429 
0430 
0431 Variable MicrobeApp::variable( const QString & name ) const
0432 {
0433     VariableList::const_iterator end = m_variables.end();
0434     for ( VariableList::const_iterator it = m_variables.begin(); it != end; ++it )
0435     {
0436         if ( (*it).name() == name )
0437             return *it;
0438     }
0439     return Variable();
0440 }
0441 
0442 
0443 bool MicrobeApp::isVariableKnown( const QString & name ) const
0444 {
0445     return variable(name).type() != Variable::invalidType;
0446 }
0447 
0448 
0449 void MicrobeApp::addDelayRoutineWanted( unsigned routine )
0450 {
0451     if ( m_maxDelaySubroutine < routine )
0452         m_maxDelaySubroutine = routine;
0453 }
0454 
0455 
0456 void MicrobeApp::addAlias( const QString & name, const QString & dest )
0457 {
0458     m_aliasList[name] = dest;
0459 }
0460 
0461 
0462 QString MicrobeApp::alias( const QString & alias ) const
0463 {
0464     // If the string is an alias, return the real string,
0465     // otherwise just return the alias as that is the real string.
0466     AliasMap::const_iterator it = m_aliasList.find(alias);
0467     if ( it != m_aliasList.constEnd() )
0468         return it.value();
0469     return alias;
0470 }
0471 
0472 
0473 void MicrobeApp::setInterruptUsed(const QString &interruptName)
0474 {
0475     // Don't add it again if it is already in the list
0476     if ( m_usedInterrupts.contains( interruptName ) )
0477         return;
0478     m_usedInterrupts.append(interruptName);
0479 }
0480 
0481 
0482 bool MicrobeApp::isInterruptUsed( const QString & interruptName )
0483 {
0484     return m_usedInterrupts.contains( interruptName );
0485 }
0486 
0487 
0488 QString MicrobeApp::dest() const
0489 {
0490     return QString("__op%1").arg(m_dest);
0491 }
0492 
0493 
0494 void MicrobeApp::incDest()
0495 {
0496     m_dest++;
0497 //  if ( ++m_dest > m_highestDest )
0498 //      m_highestDest = m_dest;
0499 }
0500 
0501 
0502 void MicrobeApp::decDest()
0503 {
0504     m_dest--;
0505 }
0506 
0507 
0508 void MicrobeApp::resetDest()
0509 {
0510     m_dest = 0;
0511 }
0512 //END class MicrobeApp
0513 
0514 
0515 
0516 //BEGIN class SourceLineMicrobe
0517 SourceLineMicrobe::SourceLineMicrobe( const QString & text, const QString & url, int line )
0518 {
0519     m_text = text;
0520     m_url = url;
0521     m_line = line;
0522 }
0523 
0524 
0525 SourceLineMicrobe::SourceLineMicrobe()
0526 {
0527     m_line = -1;
0528 }
0529 
0530 
0531 QStringList SourceLineMicrobe::toStringList( const SourceLineList & lines )
0532 {
0533     QStringList joined;
0534     SourceLineList::const_iterator end = lines.end();
0535     for ( SourceLineList::const_iterator it = lines.begin(); it != end; ++it )
0536         joined << (*it).text();
0537     return joined;
0538 
0539 }
0540 //END class SourceLineMicrobe
0541