|
|
|
/***************************************************************************
|
|
|
|
* 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 <klocale.h>
|
|
|
|
// #include <kmessagebox.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
|
|
|
|
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<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").tqarg( 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 = 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<<pinCount)-1 )
|
|
|
|
{
|
|
|
|
TQString name = *it;
|
|
|
|
name.replace("PORT","TRIS");
|
|
|
|
addCode( name+" = "+TQString::number(portType) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PORT registers
|
|
|
|
for ( TQStringList::iterator it = portNames.begin(); it != end; ++it )
|
|
|
|
{
|
|
|
|
const int portState = settings->portState(*it);
|
|
|
|
addCode( (*it)+" = "+TQString::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 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 ( ++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;
|
|
|
|
}
|
|
|
|
|