diff options
Diffstat (limited to 'src/languages/flowcode.cpp')
-rw-r--r-- | src/languages/flowcode.cpp | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/src/languages/flowcode.cpp b/src/languages/flowcode.cpp new file mode 100644 index 0000000..d19d17e --- /dev/null +++ b/src/languages/flowcode.cpp @@ -0,0 +1,496 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * [email protected] * + * * + * 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. * + ***************************************************************************/ + +#include "flowcodedocument.h" +#include "flowcode.h" +#include "flowcontainer.h" +#include "flowpart.h" +#include "microsettings.h" +#include "microinfo.h" +#include "micropackage.h" +#include "node.h" +#include "pinmapping.h" + +#include <klocale.h> +// #include <kmessagebox.h> +#include <qfile.h> + +FlowCode::FlowCode( ProcessChain *processChain, KTechlab *parent ) + : Language( processChain, parent, i18n("FlowCode") ) +{ + m_successfulMessage = i18n("*** Microbe generation successful ***"); + m_failedMessage = i18n("*** Microbe generation failed ***"); + p_startPart = 0l; +} + +FlowCode::~FlowCode() +{ +} + + +void FlowCode::processInput( ProcessOptions options ) +{ + m_processOptions = options; + + if ( !options.p_flowCodeDocument ) + { + options.p_flowCodeDocument = new FlowCodeDocument( QString::null, 0l ); + options.p_flowCodeDocument->openURL( options.inputFiles().first() ); + + connect( this, SIGNAL(processSucceeded( Language *)), options.p_flowCodeDocument, SLOT(deleteLater()) ); + connect( this, SIGNAL(processFailed( Language *)), options.p_flowCodeDocument, SLOT(deleteLater()) ); + } + + if ( !options.p_flowCodeDocument->microSettings() ) + { + finish(false); + return; + } + + QFile file(options.intermediaryOutput()); + if ( file.open(IO_WriteOnly | IO_ReadOnly) == false ) + { + finish(false); + return; + } + file.close(); + + if ( file.open(IO_WriteOnly) == false ) + { + finish(false); + return; + } + + const QString code = generateMicrobe( options.p_flowCodeDocument->itemList(), options.p_flowCodeDocument->microSettings() ); + if (code.isEmpty()) + { + finish(false); + return; + } + + QTextStream stream(&file); + stream << code; + file.close(); + finish(true); +} + + +void FlowCode::setStartPart( FlowPart *startPart ) +{ + p_startPart = startPart; +} + + +void FlowCode::addCode( const QString& code ) +{ + m_code += code; + if ( !m_code.endsWith("\n") ) m_code += '\n'; +} + +bool FlowCode::isValidBranch( FlowPart *flowPart ) +{ + return flowPart && (flowPart->level() >= m_curLevel) && !m_stopParts.contains(flowPart); +} + +void FlowCode::addCodeBranch( FlowPart * flowPart ) +{ + if (!flowPart) + return; + + if ( !isValidBranch(flowPart) ) + return; + + if ( m_addedParts.contains(flowPart) ) + { + const QString labelName = genLabel(flowPart->id()); + addCode( "goto "+labelName ); + m_gotos.append(labelName); + return; + } + else + { + m_addedParts.append(flowPart); + int prevLevel = m_curLevel; + m_curLevel = flowPart->level(); + + const QString labelName = genLabel(flowPart->id()); + addCode(labelName+':'); + m_labels.append(labelName); + + flowPart->generateMicrobe(this); + m_curLevel = prevLevel; + } +} + +QString FlowCode::genLabel( const QString &id ) +{ + return "__label_"+id; +} + +void FlowCode::addStopPart( FlowPart *part ) +{ + if (part) m_stopParts.append(part); +} + +void FlowCode::removeStopPart( FlowPart *part ) +{ + if (!part) return; + + // We only want to remove one instance of the FlowPart, in case it has been + // used as a StopPart for more than one FlowPart + FlowPartList::iterator it = m_stopParts.find(part); + if ( it != m_stopParts.end() ) m_stopParts.remove(it); +} + +QString FlowCode::generateMicrobe( const ItemList &itemList, MicroSettings *settings ) +{ + bool foundStart = false; + const ItemList::const_iterator end = itemList.end(); + for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it ) + { + if (!*it) + continue; + + FlowPart * startPart = dynamic_cast<FlowPart*>((Item*)*it); + + if (!startPart) + continue; + + // Check to see if we have any floating connections + const NodeMap nodeMap = startPart->nodeMap(); + NodeMap::const_iterator nodeMapEnd = nodeMap.end(); + for ( NodeMap::const_iterator nodeMapIt = nodeMap.begin(); nodeMapIt != nodeMapEnd; ++nodeMapIt ) + { + Node * node = nodeMapIt.data().node; + if ( !node || (node->type() != Node::fp_out) ) + continue; + + if ( !startPart->outputPart( nodeMapIt.key() ) ) + outputWarning( i18n("Warning: Floating connection for %1").arg( startPart->id() ) ); + } + + FlowContainer * fc = dynamic_cast<FlowContainer*>((Item*)*it); + + if ( (*it)->id().startsWith("START") && startPart ) + { + foundStart = true; + setStartPart(startPart); + } + else if ( ((*it)->id().startsWith("interrupt") || (*it)->id().startsWith("sub")) && fc ) + { + addSubroutine(fc); + } + } + + if (!foundStart) + { + outputError( i18n("KTechlab was unable to find the \"Start\" part.\nThis must be included as the starting point for your program.") ); + return 0; + } + + m_addedParts.clear(); + m_stopParts.clear(); + m_gotos.clear(); + m_labels.clear(); + m_code = QString::null; + + // PIC type + { + const QString codeString = settings->microInfo()->id() + "\n"; + addCode(codeString); + } + + // Initial variables + { + QStringList vars = settings->variableNames(); + + // If "inited" is true at the end, we comment at the insertion point + bool inited = false; + const QString codeString = "// Initial variable values:\n"; + addCode(codeString); + + const QStringList::iterator end = vars.end(); + for ( QStringList::iterator it = vars.begin(); it != end; ++it ) + { + VariableInfo *info = settings->variableInfo(*it); + if ( info /*&& info->initAtStart*/ ) + { + inited = true; + addCode(*it+" = "+info->valueAsString()); + } + } + if (!inited) { + m_code.remove(codeString); + } else { + addCode("\n"); + } + } + + // Initial pin maps + { + const PinMappingMap pinMappings = settings->pinMappings(); + PinMappingMap::const_iterator end = pinMappings.end(); + for ( PinMappingMap::const_iterator it = pinMappings.begin(); it != end; ++it ) + { + QString type; + + switch ( it.data().type() ) + { + case PinMapping::Keypad_4x3: + case PinMapping::Keypad_4x4: + type = "keypad"; + break; + + case PinMapping::SevenSegment: + type = "sevenseg"; + break; + + case PinMapping::Invalid: + break; + } + + if ( type.isEmpty() ) + continue; + + addCode( QString("%1 %2 %3").arg( type ).arg( it.key() ).arg( it.data().pins().join(" ") ) ); + } + } + + // Initial port settings + { + QStringList portNames = settings->microInfo()->package()->portNames(); + const QStringList::iterator end = portNames.end(); + + // TRIS registers (remember that this is set to ..11111 on all resets) + for ( QStringList::iterator it = portNames.begin(); it != end; ++it ) + { + const int portType = settings->portType(*it); + const int pinCount = settings->microInfo()->package()->pinCount( 0, *it ); + + // We don't need to reset it if portType == 2^(pinCount-1) + if ( portType != (1<<pinCount)-1 ) + { + QString name = *it; + name.replace("PORT","TRIS"); + addCode( name+" = "+QString::number(portType) ); + } + } + + // PORT registers + for ( QStringList::iterator it = portNames.begin(); it != end; ++it ) + { + const int portState = settings->portState(*it); + addCode( (*it)+" = "+QString::number(portState) ); + } + } + + + m_curLevel = p_startPart->level(); + addCodeBranch(p_startPart); + addCode("end"); + + { + const FlowPartList::iterator end = m_subroutines.end(); + for ( FlowPartList::iterator it = m_subroutines.begin(); it != end; ++it ) + { + m_curLevel = 0; + if (*it) + { + addCode("\n"); + addCodeBranch(*it); + } + } + } + + tidyCode(); + return m_code; +} + +void FlowCode::tidyCode() +{ + // First, get rid of the unused labels + const QStringList::iterator end = m_labels.end(); + for ( QStringList::iterator it = m_labels.begin(); it != end; ++it ) + { + if ( !m_gotos.contains(*it) ) m_code.remove(*it+':'); + } + + + // And now on to handling indentation :-) + + if ( !m_code.endsWith("\n") ) m_code.append("\n"); + QString newCode; + bool multiLineComment = false; // For "/*"..."*/" + bool comment = false; // For "//" + bool asmEmbed = false; + bool asmEmbedAllowed = true; + bool asmKeyword = false; + int asmEmbedLevel = -1; + int level = 0; + + int pos=-1; + const int length = m_code.length(); + while ( ++pos<length ) + { + switch ( m_code[pos].latin1() ) + { + case '\n': + { + if (comment && !multiLineComment) comment = false; + newCode += '\n'; + if ( !comment && !asmEmbed ) + { + while ( pos+1<length && m_code[pos+1].isSpace() ) pos++; + bool closeBrace = false; + if ( pos+1<length && m_code[pos+1] == '}' ) + { + level--; + pos++; + closeBrace = true; + } + for ( int i=0; i<level; i++ ) newCode += '\t'; + if (closeBrace) newCode += '}'; + asmEmbedAllowed = true; + } + break; + } + case '/': + { + newCode += '/'; + if ( pos+1<length ) + { + if ( m_code[pos+1] == '/' ) comment = true; + else if ( m_code[pos+1] == '*' ) multiLineComment = comment = true; + newCode += m_code[++pos]; + } + asmEmbedAllowed = false; + asmKeyword = false; + break; + } + case '*': + { + newCode += '*'; + if ( pos+1<length ) + { + if ( m_code[pos++] == '/' && multiLineComment ) comment = multiLineComment = false; + newCode += m_code[pos]; + } + asmEmbedAllowed = false; + asmKeyword = false; + break; + } + case '{': + { + if (asmKeyword) { + asmEmbed = true; + asmEmbedLevel = level; + } + + if ( !comment ) level++; + newCode += '{'; + + asmEmbedAllowed = false; + asmKeyword = false; + break; + } + case '}': + { + if ( !comment ) level--; + + if (asmEmbed && asmEmbedLevel == level) + { + asmEmbed = false; + newCode += "\n"; + for ( int i=0; i<level; i++ ) newCode += '\t'; + } + newCode += '}'; + + asmEmbedAllowed = true; + asmKeyword = false; + break; + } + case 'a': + { + newCode += m_code[pos]; + if ( asmEmbedAllowed && !comment && pos+2<length ) + { + if ( m_code[pos+1] == 's' && m_code[pos+2] == 'm' ) + { + asmKeyword = true; + newCode += "sm"; + pos += 2; + } + } + break; + } + default: + { + asmEmbedAllowed = false; + asmKeyword = false; + newCode += m_code[pos]; + break; + } + } + } + m_code = newCode; +} + +void FlowCode::addSubroutine( FlowPart *part ) +{ + if ( !part || m_subroutines.contains(part) || part->parentItem() || !dynamic_cast<FlowContainer*>(part) ) return; + m_subroutines.append(part); +} + + +ProcessOptions::ProcessPath::Path FlowCode::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const +{ + switch (inputPath) + { + case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: + return ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute; + + case ProcessOptions::ProcessPath::FlowCode_Microbe: + return ProcessOptions::ProcessPath::None; + + case ProcessOptions::ProcessPath::FlowCode_PIC: + return ProcessOptions::ProcessPath::Microbe_PIC; + + case ProcessOptions::ProcessPath::FlowCode_Program: + return ProcessOptions::ProcessPath::Microbe_Program; + + case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: + case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: + case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: + case ProcessOptions::ProcessPath::C_AssemblyRelocatable: + case ProcessOptions::ProcessPath::C_Library: + case ProcessOptions::ProcessPath::C_Object: + case ProcessOptions::ProcessPath::C_PIC: + case ProcessOptions::ProcessPath::C_Program: + case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Microbe_PIC: + case ProcessOptions::ProcessPath::Microbe_Program: + case ProcessOptions::ProcessPath::Object_Disassembly: + case ProcessOptions::ProcessPath::Object_Library: + case ProcessOptions::ProcessPath::Object_PIC: + case ProcessOptions::ProcessPath::Object_Program: + case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Program_Disassembly: + case ProcessOptions::ProcessPath::Program_PIC: + case ProcessOptions::ProcessPath::Invalid: + case ProcessOptions::ProcessPath::None: + return ProcessOptions::ProcessPath::Invalid; + } + + return ProcessOptions::ProcessPath::Invalid; +} + |