You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
497 lines
12 KiB
497 lines
12 KiB
15 years ago
|
/***************************************************************************
|
||
|
* 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 <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;
|
||
|
}
|
||
|
|