File indexing completed on 2024-04-21 13:20:25

0001 /***************************************************************************
0002  *   Copyright (C) 2003-2005 by David Saxton                               *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "flowcode.h"
0012 #include "flowcodedocument.h"
0013 #include "flowcontainer.h"
0014 #include "flowpart.h"
0015 #include "microinfo.h"
0016 #include "micropackage.h"
0017 #include "microsettings.h"
0018 #include "node.h"
0019 #include "outputflownode.h"
0020 #include "pinmapping.h"
0021 
0022 #include <KLocalizedString>
0023 // #include <KMessageBox>
0024 #include <QFile>
0025 #include <QTextStream>
0026 
0027 FlowCode::FlowCode(ProcessChain *processChain)
0028     : Language(processChain, i18n("FlowCode"))
0029 {
0030     m_successfulMessage = i18n("*** Microbe generation successful ***");
0031     m_failedMessage = i18n("*** Microbe generation failed ***");
0032     p_startPart = nullptr;
0033 }
0034 
0035 FlowCode::~FlowCode()
0036 {
0037 }
0038 
0039 void FlowCode::processInput(ProcessOptions options)
0040 {
0041     m_processOptions = options;
0042 
0043     if (!options.p_flowCodeDocument) {
0044         options.p_flowCodeDocument = new FlowCodeDocument(QString());
0045         options.p_flowCodeDocument->openURL(QUrl::fromLocalFile(options.inputFiles().first()));
0046 
0047         connect(this, SIGNAL(processSucceeded(Language *)), options.p_flowCodeDocument, SLOT(deleteLater()));
0048         connect(this, SIGNAL(processFailed(Language *)), options.p_flowCodeDocument, SLOT(deleteLater()));
0049     }
0050 
0051     if (!options.p_flowCodeDocument->microSettings()) {
0052         finish(false);
0053         return;
0054     }
0055 
0056     QFile file(options.intermediaryOutput());
0057     if (file.open(QIODevice::WriteOnly | QIODevice::ReadOnly) == false) {
0058         finish(false);
0059         return;
0060     }
0061     file.close();
0062 
0063     if (file.open(QIODevice::WriteOnly) == false) {
0064         finish(false);
0065         return;
0066     }
0067 
0068     const QString code = generateMicrobe(options.p_flowCodeDocument->itemList(), options.p_flowCodeDocument->microSettings());
0069     if (code.isEmpty()) {
0070         finish(false);
0071         return;
0072     }
0073 
0074     QTextStream stream(&file);
0075     stream << code;
0076     file.close();
0077     finish(true);
0078 }
0079 
0080 void FlowCode::setStartPart(FlowPart *startPart)
0081 {
0082     p_startPart = startPart;
0083 }
0084 
0085 void FlowCode::addCode(const QString &code)
0086 {
0087     m_code += code;
0088     if (!m_code.endsWith("\n"))
0089         m_code += '\n';
0090 }
0091 
0092 bool FlowCode::isValidBranch(FlowPart *flowPart)
0093 {
0094     return flowPart && (flowPart->level() >= m_curLevel) && !m_stopParts.contains(flowPart);
0095 }
0096 
0097 void FlowCode::addCodeBranch(FlowPart *flowPart)
0098 {
0099     if (!flowPart)
0100         return;
0101 
0102     if (!isValidBranch(flowPart))
0103         return;
0104 
0105     if (m_addedParts.contains(flowPart)) {
0106         const QString labelName = genLabel(flowPart->id());
0107         addCode("goto " + labelName);
0108         m_gotos.append(labelName);
0109         return;
0110     } else {
0111         m_addedParts.append(flowPart);
0112         int prevLevel = m_curLevel;
0113         m_curLevel = flowPart->level();
0114 
0115         const QString labelName = genLabel(flowPart->id());
0116         addCode(labelName + ':');
0117         m_labels.append(labelName);
0118 
0119         flowPart->generateMicrobe(this);
0120         m_curLevel = prevLevel;
0121     }
0122 }
0123 
0124 QString FlowCode::genLabel(const QString &id)
0125 {
0126     return "__label_" + id;
0127 }
0128 
0129 void FlowCode::addStopPart(FlowPart *part)
0130 {
0131     if (part)
0132         m_stopParts.append(part);
0133 }
0134 
0135 void FlowCode::removeStopPart(FlowPart *part)
0136 {
0137     if (!part)
0138         return;
0139 
0140     // We only want to remove one instance of the FlowPart, in case it has been
0141     // used as a StopPart for more than one FlowPart
0142     // FlowPartList::iterator it = m_stopParts.find(part);  // 2018.12.01
0143     // if ( it != m_stopParts.end() ) m_stopParts.remove(it);
0144     int foundIndex = m_stopParts.indexOf(part);
0145     if (-1 == foundIndex) {
0146         m_stopParts.removeAll(part);
0147     }
0148 }
0149 
0150 QString FlowCode::generateMicrobe(const ItemList &itemList, MicroSettings *settings)
0151 {
0152     bool foundStart = false;
0153     const ItemList::const_iterator end = itemList.end();
0154     for (ItemList::const_iterator it = itemList.begin(); it != end; ++it) {
0155         if (!*it)
0156             continue;
0157 
0158         FlowPart *startPart = dynamic_cast<FlowPart *>(static_cast<Item *>(*it));
0159 
0160         if (!startPart)
0161             continue;
0162 
0163         // Check to see if we have any floating connections
0164         const NodeInfoMap nodeMap = startPart->nodeMap();
0165         NodeInfoMap::const_iterator nodeMapEnd = nodeMap.end();
0166         for (NodeInfoMap::const_iterator nodeMapIt = nodeMap.begin(); nodeMapIt != nodeMapEnd; ++nodeMapIt) {
0167             Node *node = nodeMapIt.value().node;
0168             // FIXME dynamic_cast used
0169             if (!node || (dynamic_cast<OutputFlowNode *>(node) == nullptr))
0170                 continue;
0171 
0172             if (!startPart->outputPart(nodeMapIt.key()))
0173                 outputWarning(i18n("Warning: Floating connection for %1", startPart->id()));
0174         }
0175 
0176         FlowContainer *fc = dynamic_cast<FlowContainer *>(static_cast<Item *>(*it));
0177 
0178         if ((*it)->id().startsWith("START") && startPart) {
0179             foundStart = true;
0180             setStartPart(startPart);
0181         } else if (((*it)->id().startsWith("interrupt") || (*it)->id().startsWith("sub")) && fc) {
0182             addSubroutine(fc);
0183         }
0184     }
0185 
0186     if (!foundStart) {
0187         outputError(i18n("KTechlab was unable to find the \"Start\" part.\nThis must be included as the starting point for your program."));
0188         return nullptr;
0189     }
0190 
0191     m_addedParts.clear();
0192     m_stopParts.clear();
0193     m_gotos.clear();
0194     m_labels.clear();
0195     m_code = QString();
0196 
0197     // PIC type
0198     {
0199         const QString codeString = settings->microInfo()->id() + "\n";
0200         addCode(codeString);
0201     }
0202 
0203     // Initial variables
0204     {
0205         QStringList vars = settings->variableNames();
0206 
0207         // If "inited" is true at the end, we comment at the insertion point
0208         bool inited = false;
0209         const QString codeString = "// Initial variable values:\n";
0210         addCode(codeString);
0211 
0212         const QStringList::iterator end = vars.end();
0213         for (QStringList::iterator it = vars.begin(); it != end; ++it) {
0214             VariableInfo *info = settings->variableInfo(*it);
0215             if (info /*&& info->initAtStart*/) {
0216                 inited = true;
0217                 addCode(*it + " = " + info->valueAsString());
0218             }
0219         }
0220         if (!inited) {
0221             m_code.remove(codeString);
0222         } else {
0223             addCode("\n");
0224         }
0225     }
0226 
0227     // Initial pin maps
0228     {
0229         const PinMappingMap pinMappings = settings->pinMappings();
0230         PinMappingMap::const_iterator end = pinMappings.end();
0231         for (PinMappingMap::const_iterator it = pinMappings.begin(); it != end; ++it) {
0232             QString type;
0233 
0234             switch (it.value().type()) {
0235             case PinMapping::Keypad_4x3:
0236             case PinMapping::Keypad_4x4:
0237                 type = "keypad";
0238                 break;
0239 
0240             case PinMapping::SevenSegment:
0241                 type = "sevenseg";
0242                 break;
0243 
0244             case PinMapping::Invalid:
0245                 break;
0246             }
0247 
0248             if (type.isEmpty())
0249                 continue;
0250 
0251             addCode(QString("%1 %2 %3").arg(type).arg(it.key()).arg(it.value().pins().join(" ")));
0252         }
0253     }
0254 
0255     // Initial port settings
0256     {
0257         QStringList portNames = settings->microInfo()->package()->portNames();
0258         const QStringList::iterator end = portNames.end();
0259 
0260         // TRIS registers (remember that this is set to ..11111 on all resets)
0261         for (QStringList::iterator it = portNames.begin(); it != end; ++it) {
0262             const int portType = settings->portType(*it);
0263             const int pinCount = settings->microInfo()->package()->pinCount(0, *it);
0264 
0265             // We don't need to reset it if portType == 2^(pinCount-1)
0266             if (portType != (1 << pinCount) - 1) {
0267                 QString name = *it;
0268                 name.replace("PORT", "TRIS");
0269                 addCode(name + " = " + QString::number(portType));
0270             }
0271         }
0272 
0273         // PORT registers
0274         for (QStringList::iterator it = portNames.begin(); it != end; ++it) {
0275             const int portState = settings->portState(*it);
0276             addCode((*it) + " = " + QString::number(portState));
0277         }
0278     }
0279 
0280     m_curLevel = p_startPart->level();
0281     addCodeBranch(p_startPart);
0282     addCode("end");
0283 
0284     {
0285         const FlowPartList::iterator end = m_subroutines.end();
0286         for (FlowPartList::iterator it = m_subroutines.begin(); it != end; ++it) {
0287             m_curLevel = 0;
0288             if (*it) {
0289                 addCode("\n");
0290                 addCodeBranch(*it);
0291             }
0292         }
0293     }
0294 
0295     tidyCode();
0296     return m_code;
0297 }
0298 
0299 void FlowCode::tidyCode()
0300 {
0301     // First, get rid of the unused labels
0302     const QStringList::iterator end = m_labels.end();
0303     for (QStringList::iterator it = m_labels.begin(); it != end; ++it) {
0304         if (!m_gotos.contains(*it))
0305             m_code.remove(*it + ':');
0306     }
0307 
0308     // And now on to handling indentation :-)
0309 
0310     if (!m_code.endsWith("\n"))
0311         m_code.append("\n");
0312     QString newCode;
0313     bool multiLineComment = false; // For "/*"..."*/"
0314     bool comment = false;          // For "//"
0315     bool asmEmbed = false;
0316     bool asmEmbedAllowed = true;
0317     bool asmKeyword = false;
0318     int asmEmbedLevel = -1;
0319     int level = 0;
0320 
0321     int pos = -1;
0322     const int length = m_code.length();
0323     while (++pos < length) {
0324         switch (m_code[pos].toLatin1()) {
0325         case '\n': {
0326             if (comment && !multiLineComment)
0327                 comment = false;
0328             newCode += '\n';
0329             if (!comment && !asmEmbed) {
0330                 while (pos + 1 < length && m_code[pos + 1].isSpace())
0331                     pos++;
0332                 bool closeBrace = false;
0333                 if (pos + 1 < length && m_code[pos + 1] == '}') {
0334                     level--;
0335                     pos++;
0336                     closeBrace = true;
0337                 }
0338                 for (int i = 0; i < level; i++)
0339                     newCode += '\t';
0340                 if (closeBrace)
0341                     newCode += '}';
0342                 asmEmbedAllowed = true;
0343             }
0344             break;
0345         }
0346         case '/': {
0347             newCode += '/';
0348             if (pos + 1 < length) {
0349                 if (m_code[pos + 1] == '/')
0350                     comment = true;
0351                 else if (m_code[pos + 1] == '*')
0352                     multiLineComment = comment = true;
0353                 newCode += m_code[++pos];
0354             }
0355             asmEmbedAllowed = false;
0356             asmKeyword = false;
0357             break;
0358         }
0359         case '*': {
0360             newCode += '*';
0361             if (pos + 1 < length) {
0362                 if (m_code[pos++] == '/' && multiLineComment)
0363                     comment = multiLineComment = false;
0364                 newCode += m_code[pos];
0365             }
0366             asmEmbedAllowed = false;
0367             asmKeyword = false;
0368             break;
0369         }
0370         case '{': {
0371             if (asmKeyword) {
0372                 asmEmbed = true;
0373                 asmEmbedLevel = level;
0374             }
0375 
0376             if (!comment)
0377                 level++;
0378             newCode += '{';
0379 
0380             asmEmbedAllowed = false;
0381             asmKeyword = false;
0382             break;
0383         }
0384         case '}': {
0385             if (!comment)
0386                 level--;
0387 
0388             if (asmEmbed && asmEmbedLevel == level) {
0389                 asmEmbed = false;
0390                 newCode += "\n";
0391                 for (int i = 0; i < level; i++)
0392                     newCode += '\t';
0393             }
0394             newCode += '}';
0395 
0396             asmEmbedAllowed = true;
0397             asmKeyword = false;
0398             break;
0399         }
0400         case 'a': {
0401             newCode += m_code[pos];
0402             if (asmEmbedAllowed && !comment && pos + 2 < length) {
0403                 if (m_code[pos + 1] == 's' && m_code[pos + 2] == 'm') {
0404                     asmKeyword = true;
0405                     newCode += "sm";
0406                     pos += 2;
0407                 }
0408             }
0409             break;
0410         }
0411         default: {
0412             asmEmbedAllowed = false;
0413             asmKeyword = false;
0414             newCode += m_code[pos];
0415             break;
0416         }
0417         }
0418     }
0419     m_code = newCode;
0420 }
0421 
0422 void FlowCode::addSubroutine(FlowPart *part)
0423 {
0424     if (!part || m_subroutines.contains(part) || part->parentItem() || !dynamic_cast<FlowContainer *>(part))
0425         return;
0426     m_subroutines.append(part);
0427 }
0428 
0429 ProcessOptions::ProcessPath::Path FlowCode::outputPath(ProcessOptions::ProcessPath::Path inputPath) const
0430 {
0431     switch (inputPath) {
0432     case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute:
0433         return ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute;
0434 
0435     case ProcessOptions::ProcessPath::FlowCode_Microbe:
0436         return ProcessOptions::ProcessPath::None;
0437 
0438     case ProcessOptions::ProcessPath::FlowCode_PIC:
0439         return ProcessOptions::ProcessPath::Microbe_PIC;
0440 
0441     case ProcessOptions::ProcessPath::FlowCode_Program:
0442         return ProcessOptions::ProcessPath::Microbe_Program;
0443 
0444     case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC:
0445     case ProcessOptions::ProcessPath::AssemblyAbsolute_Program:
0446     case ProcessOptions::ProcessPath::AssemblyRelocatable_Library:
0447     case ProcessOptions::ProcessPath::AssemblyRelocatable_Object:
0448     case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC:
0449     case ProcessOptions::ProcessPath::AssemblyRelocatable_Program:
0450     case ProcessOptions::ProcessPath::C_AssemblyRelocatable:
0451     case ProcessOptions::ProcessPath::C_Library:
0452     case ProcessOptions::ProcessPath::C_Object:
0453     case ProcessOptions::ProcessPath::C_PIC:
0454     case ProcessOptions::ProcessPath::C_Program:
0455     case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute:
0456     case ProcessOptions::ProcessPath::Microbe_PIC:
0457     case ProcessOptions::ProcessPath::Microbe_Program:
0458     case ProcessOptions::ProcessPath::Object_Disassembly:
0459     case ProcessOptions::ProcessPath::Object_Library:
0460     case ProcessOptions::ProcessPath::Object_PIC:
0461     case ProcessOptions::ProcessPath::Object_Program:
0462     case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute:
0463     case ProcessOptions::ProcessPath::Program_Disassembly:
0464     case ProcessOptions::ProcessPath::Program_PIC:
0465     case ProcessOptions::ProcessPath::Invalid:
0466     case ProcessOptions::ProcessPath::None:
0467         return ProcessOptions::ProcessPath::Invalid;
0468     }
0469 
0470     return ProcessOptions::ProcessPath::Invalid;
0471 }