File indexing completed on 2024-05-05 05:46:08

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 "config.h"
0012 #ifndef NO_GPSIM
0013 
0014 #include "canvasitemparts.h"
0015 #include "circuitdocument.h"
0016 #include "docmanager.h"
0017 #include "gpsimprocessor.h"
0018 #include "ktechlab.h"
0019 #include "libraryitem.h"
0020 #include "logic.h"
0021 #include "microlibrary.h"
0022 #include "micropackage.h"
0023 #include "piccomponent.h"
0024 #include "piccomponentpin.h"
0025 #include "picinfo.h"
0026 #include "projectmanager.h"
0027 
0028 #include <KLocalizedString>
0029 #include <KMessageBox>
0030 
0031 #include <QIcon>
0032 #include <QPointer>
0033 #include <QStringList>
0034 
0035 #include "gpsim/ioports.h"
0036 #include "gpsim/pic-processor.h"
0037 
0038 #include <ktechlab_debug.h>
0039 
0040 QString PICComponent::_def_PICComponent_fileName;
0041 
0042 Item *PICComponent::construct(ItemDocument *itemDocument, bool newItem, const char *id)
0043 {
0044     return new PICComponent(static_cast<ICNDocument *>(itemDocument), newItem, id);
0045 }
0046 
0047 LibraryItem *PICComponent::libraryItem()
0048 {
0049     QStringList IDs;
0050     IDs << "ec/pic"
0051         << "ec/picitem"
0052         << "ec/picitem_18pin";
0053 
0054     return new LibraryItem(IDs, "PIC", i18n("Integrated Circuits"), "ic2.png", LibraryItem::lit_component, PICComponent::construct);
0055 }
0056 
0057 PICComponent::PICComponent(ICNDocument *icnDocument, bool newItem, const char *id)
0058     : Component(icnDocument, newItem, id ? id : "pic")
0059 {
0060     m_name = i18n("PIC Micro");
0061 
0062     if (_def_PICComponent_fileName.isEmpty())
0063         _def_PICComponent_fileName = i18n("<Enter location of PIC Program>");
0064 
0065     m_bCreatedInitialPackage = false;
0066     m_bLoadingProgram = false;
0067     m_pGpsim = nullptr;
0068 
0069     addButton("run", QRect(), QIcon::fromTheme("media-playback-start"));
0070     addButton("pause", QRect(), QIcon::fromTheme("media-playback-pause"));
0071     addButton("reset", QRect(), QIcon::fromTheme("process-stop"));
0072     addButton("reload", QRect(), QIcon::fromTheme("view-refresh"));
0073 
0074     connect(KTechlab::self(), &KTechlab::recentFileAdded, this, &PICComponent::slotUpdateFileList);
0075 
0076     connect(ProjectManager::self(), &ProjectManager::projectOpened, this, &PICComponent::slotUpdateFileList);
0077     connect(ProjectManager::self(), &ProjectManager::projectClosed, this, &PICComponent::slotUpdateFileList);
0078     connect(ProjectManager::self(), &ProjectManager::projectCreated, this, &PICComponent::slotUpdateFileList);
0079     connect(ProjectManager::self(), &ProjectManager::subprojectCreated, this, &PICComponent::slotUpdateFileList);
0080     connect(ProjectManager::self(), &ProjectManager::filesAdded, this, &PICComponent::slotUpdateFileList);
0081     connect(ProjectManager::self(), &ProjectManager::filesRemoved, this, &PICComponent::slotUpdateFileList);
0082 
0083     createProperty("program", Variant::Type::FileName);
0084     property("program")->setCaption(i18n("Program"));
0085     const FileFilters fileFilters({
0086         {i18n("All Supported Files"), QStringLiteral("*.flowcode *.cod *.asm *.basic *.microbe *.c")},
0087         {i18n("FlowCode") + QLatin1String(" (*.flowcode)"),        QStringLiteral("*.flowcode")},
0088         {i18n("Symbol File") + QLatin1String(" (*.cod)"),          QStringLiteral("*.cod")},
0089         {i18n("Assembly Code") + QLatin1String(" (*.asm)"),        QStringLiteral("*.asm")},
0090         {i18n("Microbe") + QLatin1String(" (*.basic, *.microbe)"), QStringLiteral("*.basic *.microbe")},
0091         {i18n("C Code") + QLatin1String(" (*.c)"),                 QStringLiteral("*.c")},
0092         {i18n("All Files"), QStringLiteral("*")},
0093     });
0094     property("program")->setFileFilters(fileFilters);
0095 
0096     // Used for restoring the pins on file loading before we have had a change
0097     // to compile the PIC program
0098     createProperty("lastPackage", Variant::Type::String);
0099     property("lastPackage")->setHidden(true);
0100 
0101     //  //HACK This is to enable loading with pre-0.3 files (which didn't set a "lastPackage"
0102     //  // property). This will allow a P16F84 PIC to be initialized (which agrees with pre-0.3
0103     //  // behaviour), but it will also load it if
0104 
0105     // This to allow loading of the PIC component from pre-0.3 files (which didn't set a
0106     // "lastPackage" property).
0107     if (!newItem)
0108         property("lastPackage")->setValue("P16F84");
0109 
0110     slotUpdateFileList();
0111     slotUpdateBtns();
0112 
0113     initPackage(nullptr);
0114 }
0115 
0116 PICComponent::~PICComponent()
0117 {
0118     deletePICComponentPins();
0119     delete m_pGpsim;
0120 }
0121 
0122 void PICComponent::dataChanged()
0123 {
0124     qCDebug(KTL_LOG);
0125     initPIC(false);
0126 }
0127 
0128 void PICComponent::initPIC(bool forceReload)
0129 {
0130     if (!m_bCreatedInitialPackage) {
0131         qCDebug(KTL_LOG) << " creating initial package";
0132         // We are still being created, so other connectors will be expecting us to
0133         // have grown pins soonish.
0134         MicroInfo *microInfo = MicroLibrary::self()->microInfoWithID(dataString("lastPackage"));
0135         if (microInfo) {
0136             initPackage(microInfo);
0137         } else {
0138             qCDebug(KTL_LOG) << " unknown last package: " << dataString("lastPackage");
0139         }
0140     }
0141 
0142     QString newProgram = dataString("program");
0143     qCDebug(KTL_LOG) << "newProgram=" << newProgram;
0144     bool newFile = (m_picFile != newProgram);
0145     if (!newFile && !forceReload) {
0146         qCDebug(KTL_LOG) << "not new program, not force reload, exiting";
0147         return;
0148     }
0149 
0150     delete m_pGpsim;
0151     m_pGpsim = nullptr;
0152 
0153     switch (GpsimProcessor::isValidProgramFile(newProgram)) {
0154     case GpsimProcessor::DoesntExist:
0155         if (newProgram == _def_PICComponent_fileName && !newProgram.isEmpty())
0156             break;
0157         KMessageBox::error(nullptr, i18n("The file \"%1\" does not exist.", newProgram));
0158         m_picFile = QString();
0159         break;
0160 
0161     case GpsimProcessor::IncorrectType:
0162         if (newProgram == _def_PICComponent_fileName && !newProgram.isEmpty())
0163             break;
0164         KMessageBox::error(nullptr,
0165                            i18n("\"%1\" is not a valid PIC program.\nThe file must exist, and the extension should be \".cod\", \".asm\", \".flowcode\", \".basic\", \".microbe\" or \".c\".\n\".hex\" is allowed, provided that there is a "
0166                                 "corresponding \".cod\" file.",
0167                                 newProgram));
0168         m_picFile = QString();
0169         break;
0170 
0171     case GpsimProcessor::Valid:
0172         m_picFile = newProgram;
0173         m_symbolFile = createSymbolFile();
0174         break;
0175     }
0176 
0177     slotUpdateBtns();
0178 }
0179 
0180 void PICComponent::deletePICComponentPins()
0181 {
0182     qDeleteAll(m_picComponentPinMap);
0183     m_picComponentPinMap.clear();
0184 }
0185 
0186 void PICComponent::initPackage(MicroInfo *microInfo)
0187 {
0188     MicroPackage *microPackage = microInfo ? microInfo->package() : nullptr;
0189 
0190     if (microPackage) {
0191         m_bCreatedInitialPackage = true;
0192 
0193         // BEGIN Get pin IDs
0194         QStringList allPinIDs = microPackage->pinIDs();
0195         QStringList ioPinIDs = microPackage->pinIDs(PicPin::type_bidir | PicPin::type_input | PicPin::type_open);
0196 
0197         // Now, we make the unwanted pin ids blank, so a pin is not created for them
0198         const QStringList::iterator allPinIDsEnd = allPinIDs.end();
0199         for (QStringList::iterator it = allPinIDs.begin(); it != allPinIDsEnd; ++it) {
0200             if (!ioPinIDs.contains(*it))
0201                 *it = "";
0202         }
0203         // END Get pin IDs
0204 
0205         // BEGIN Remove old stuff
0206         // Remove old text
0207         TextMap textMapCopy = m_textMap;
0208         const TextMap::iterator textMapEnd = textMapCopy.end();
0209         for (TextMap::iterator it = textMapCopy.begin(); it != textMapEnd; ++it)
0210             removeDisplayText(it.key());
0211 
0212         // Remove the old pins
0213         deletePICComponentPins();
0214 
0215         // Remove old nodes
0216         NodeInfoMap nodeMapCopy = m_nodeMap;
0217         const NodeInfoMap::iterator nodeMapEnd = nodeMapCopy.end();
0218         for (NodeInfoMap::iterator it = nodeMapCopy.begin(); it != nodeMapEnd; ++it) {
0219             if (!ioPinIDs.contains(it.key()))
0220                 removeNode(it.key());
0221         }
0222 
0223         removeElements();
0224         // END Remove old stuff
0225 
0226         // BEGIN Create new stuff
0227         initDIPSymbol(allPinIDs, 80);
0228         initDIP(allPinIDs);
0229 
0230         PicPinMap picPinMap = microPackage->pins(PicPin::type_bidir | PicPin::type_input | PicPin::type_open);
0231         const PicPinMap::iterator picPinMapEnd = picPinMap.end();
0232         for (PicPinMap::iterator it = picPinMap.begin(); it != picPinMapEnd; ++it)
0233             m_picComponentPinMap[it.key()] = new PICComponentPin(this, it.value());
0234         // END Create new stuff
0235 
0236         removeDisplayText("no_file");
0237         addDisplayText("picid", QRect(offsetX(), offsetY() - 16, width(), 16), microInfo->id());
0238     } else {
0239         setSize(-48, -72, 96, 144);
0240         removeDisplayText("picid");
0241         addDisplayText("no_file", sizeRect(), i18n("(No\nprogram\nloaded)"));
0242     }
0243 
0244     // BEGIN Update button positions
0245     int leftpos = (width() - 88) / 2 + offsetX();
0246     button("run")->setOriginalRect(QRect(leftpos, height() + 4 + offsetY(), 20, 20));
0247     button("pause")->setOriginalRect(QRect(leftpos + 23, height() + 4 + offsetY(), 20, 20));
0248     button("reset")->setOriginalRect(QRect(leftpos + 46, height() + 4 + offsetY(), 20, 20));
0249     button("reload")->setOriginalRect(QRect(leftpos + 69, height() + 4 + offsetY(), 20, 20));
0250     updateAttachedPositioning();
0251     // END Update button positions
0252 }
0253 
0254 void PICComponent::attachPICComponentPins()
0255 {
0256     if (!m_pGpsim || !m_pGpsim->picProcessor())
0257         return;
0258 
0259     pic_processor *picProcessor = m_pGpsim->picProcessor();
0260 
0261     const PICComponentPinMap::iterator end = m_picComponentPinMap.end();
0262     for (PICComponentPinMap::iterator it = m_picComponentPinMap.begin(); it != end; ++it)
0263         it.value()->attach(picProcessor->get_pin(it.key()));
0264 }
0265 
0266 void PICComponent::slotUpdateFileList()
0267 {
0268     QList<QUrl> preFileList = KTechlab::self()->recentFiles();
0269 
0270     QStringList fileList;
0271 
0272     if (ProjectInfo *info = ProjectManager::self()->currentProject()) {
0273         const QList<QUrl> urls = info->childOutputURLs(ProjectItem::AllTypes, ProjectItem::ProgramOutput);
0274         for (const QUrl &url : urls)
0275             fileList << url.toLocalFile();
0276     }
0277 
0278     const QList<QUrl>::iterator end = preFileList.end();
0279     for (QList<QUrl>::iterator it = preFileList.begin(); it != end; ++it) {
0280         const QUrl recentFile = *it;
0281         if (!recentFile.isLocalFile())
0282             continue;
0283         const QString file = recentFile.toLocalFile();
0284         if ((file.endsWith(".flowcode") || file.endsWith(".asm") || file.endsWith(".cod") || file.endsWith(".basic") || file.endsWith(".microbe")) && !fileList.contains(file)) {
0285             fileList.append(file);
0286         }
0287     }
0288 
0289     QString fileName = dataString("program");
0290 
0291     property("program")->setAllowed(fileList);
0292     property("program")->setValue(fileName.isEmpty() ? _def_PICComponent_fileName : fileName);
0293 }
0294 
0295 void PICComponent::buttonStateChanged(const QString &id, bool state)
0296 {
0297     if (!state)
0298         return;
0299 
0300     if (id == "reload") {
0301         programReload();
0302         return;
0303     }
0304 
0305     if (!m_pGpsim)
0306         return;
0307 
0308     if (id == "run")
0309         m_pGpsim->setRunning(true);
0310 
0311     else if (id == "pause")
0312         m_pGpsim->setRunning(false);
0313 
0314     else if (id == "reset") {
0315         m_pGpsim->reset();
0316 
0317         // Set all pin outputs to low
0318         const PICComponentPinMap::iterator end = m_picComponentPinMap.end();
0319         for (PICComponentPinMap::iterator it = m_picComponentPinMap.begin(); it != end; ++it)
0320             it.value()->resetOutput();
0321     }
0322 
0323     slotUpdateBtns();
0324 }
0325 
0326 bool PICComponent::mouseDoubleClickEvent(const EventInfo &eventInfo)
0327 {
0328     Q_UNUSED(eventInfo);
0329     if (m_picFile.isEmpty() || (m_picFile == _def_PICComponent_fileName))
0330         return false;
0331 
0332     (void)DocManager::self()->openURL(QUrl::fromLocalFile(m_picFile));
0333 
0334     return true;
0335 }
0336 
0337 QString PICComponent::createSymbolFile()
0338 {
0339     qCDebug(KTL_LOG);
0340     m_bLoadingProgram = true;
0341     slotUpdateBtns();
0342 
0343     return GpsimProcessor::generateSymbolFile(dataString("program"), this, SLOT(slotCODCreationSucceeded()), SLOT(slotCODCreationFailed()));
0344 }
0345 
0346 void PICComponent::slotCODCreationSucceeded()
0347 {
0348     qCDebug(KTL_LOG) << " m_symbolFile=" << m_symbolFile;
0349     m_bLoadingProgram = false;
0350 
0351     delete m_pGpsim;
0352     m_pGpsim = new GpsimProcessor(m_symbolFile);
0353 
0354     if (m_pGpsim->codLoadStatus() == GpsimProcessor::CodSuccess) {
0355         MicroInfo *microInfo = m_pGpsim->microInfo();
0356         if (!microInfo) {
0357             // FIXME we should be select somehow the type of the PIC. this is only a stability hack.
0358             qCWarning(KTL_LOG) << "cannot identify the PIC, defaulting to P16F84";
0359             microInfo = MicroLibrary::self()->microInfoWithID("P16F84");
0360         }
0361         property("lastPackage")->setValue(microInfo->id());
0362         initPackage(microInfo);
0363 
0364         connect(m_pGpsim, &GpsimProcessor::runningStatusChanged, this, &PICComponent::slotUpdateBtns);
0365         attachPICComponentPins();
0366     }
0367 
0368     else {
0369         m_pGpsim->displayCodLoadStatus();
0370         delete m_pGpsim;
0371         m_pGpsim = nullptr;
0372     }
0373 
0374     slotUpdateBtns();
0375 }
0376 
0377 void PICComponent::slotCODCreationFailed()
0378 {
0379     m_bLoadingProgram = false;
0380     slotUpdateBtns();
0381 }
0382 
0383 void PICComponent::programReload()
0384 {
0385     qCDebug(KTL_LOG);
0386 
0387     delete m_pGpsim;
0388     m_pGpsim = nullptr;
0389 
0390     initPIC(true);
0391 
0392     slotUpdateBtns();
0393 }
0394 
0395 void PICComponent::slotUpdateBtns()
0396 {
0397     // We can get called by the destruction of gpsim after our canvas has been set to nullptr
0398     if (!canvas()) {
0399         qCDebug(KTL_LOG) << " no canvas, exiting";
0400         return;
0401     }
0402 
0403     button("run")->setEnabled(m_pGpsim && !m_pGpsim->isRunning());
0404     button("pause")->setEnabled(m_pGpsim && m_pGpsim->isRunning());
0405     button("reset")->setEnabled(m_pGpsim);
0406     button("reload")->setEnabled(!m_bLoadingProgram && (dataString("program") != _def_PICComponent_fileName));
0407 
0408     canvas()->setChanged(button("run")->boundingRect());
0409     canvas()->setChanged(button("pause")->boundingRect());
0410     canvas()->setChanged(button("reset")->boundingRect());
0411     canvas()->setChanged(button("reload")->boundingRect());
0412 }
0413 
0414 #include "moc_piccomponent.cpp"
0415 
0416 #endif