File indexing completed on 2025-04-27 05:16:32
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 }