/*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 // #include #include FlowCode::FlowCode( ProcessChain *processChain, KTechlab *tqparent ) : Language( processChain, tqparent, 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( TQString(), 0l ); options.p_flowCodeDocument->openURL( options.inputFiles().first() ); connect( this, TQT_SIGNAL(processSucceeded( Language *)), options.p_flowCodeDocument, TQT_SLOT(deleteLater()) ); connect( this, TQT_SIGNAL(processFailed( Language *)), options.p_flowCodeDocument, TQT_SLOT(deleteLater()) ); } if ( !options.p_flowCodeDocument->microSettings() ) { finish(false); return; } TQFile 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 TQString code = generateMicrobe( options.p_flowCodeDocument->itemList(), options.p_flowCodeDocument->microSettings() ); if (code.isEmpty()) { finish(false); return; } TQTextStream stream(&file); stream << code; file.close(); finish(true); } void FlowCode::setStartPart( FlowPart *startPart ) { p_startPart = startPart; } void FlowCode::addCode( const TQString& 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 TQString 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 TQString labelName = genLabel(flowPart->id()); addCode(labelName+':'); m_labels.append(labelName); flowPart->generateMicrobe(this); m_curLevel = prevLevel; } } TQString FlowCode::genLabel( const TQString &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); } TQString 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((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").tqarg( startPart->id() ) ); } FlowContainer * fc = dynamic_cast((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 = TQString(); // PIC type { const TQString codeString = settings->microInfo()->id() + "\n"; addCode(codeString); } // Initial variables { TQStringList vars = settings->variableNames(); // If "inited" is true at the end, we comment at the insertion point bool inited = false; const TQString codeString = "// Initial variable values:\n"; addCode(codeString); const TQStringList::iterator end = vars.end(); for ( TQStringList::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 ) { TQString 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( TQString("%1 %2 %3").tqarg( type ).tqarg( it.key() ).tqarg( it.data().pins().join(" ") ) ); } } // Initial port settings { TQStringList portNames = settings->microInfo()->package()->portNames(); const TQStringList::iterator end = portNames.end(); // TRIS registers (remember that this is set to ..11111 on all resets) for ( TQStringList::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<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 TQStringList::iterator end = m_labels.end(); for ( TQStringList::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"); TQString 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 ( ++posparentItem() || !dynamic_cast(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; }