/***************************************************************************
 *   Copyright (C) 2020 by Santiago González                               *
 *                                                                         *
 ***( see copyright.txt file at root folder )*******************************/

#include <QDomDocument>
#include <QFileInfo>
#include <QDebug>
#include <QObject>
#include <math.h>

#include "mcucreator.h"
#include "circuitwidget.h"
#include "circuit.h"
#include "subcircuit.h"
#include "datautils.h"
#include "regwatcher.h"
#include "e_mcu.h"
#include "mcu.h"
#include "mcuport.h"
#include "ioport.h"
#include "mcupin.h"
#include "usarttx.h"
#include "usartrx.h"
#include "watcher.h"
#include "console.h"

// Cores
#include "avrcore.h"
#include "avrport.h"
#include "avrtimer.h"
#include "avrocunit.h"
#include "avrocm.h"
#include "avricunit.h"
#include "avrinterrupt.h"
#include "avrusart.h"
#include "avradc.h"
#include "avrtwi.h"
#include "avrspi.h"
#include "avrusi.h"
#include "avrwdt.h"
#include "avreeprom.h"
#include "avrcomparator.h"
#include "avrsleep.h"
#include "avrintosc.h"

#include "pic12core.h"
#include "pic14core.h"
#include "pic14ecore.h"
#include "picport.h"
#include "pictimer.h"
#include "picccpunit.h"
#include "picicunit.h"
#include "picinterrupt.h"
#include "pic14einterrupt.h"
#include "picusart.h"
#include "picmssp.h"
#include "pictwi.h"
#include "picspi.h"
#include "picadc.h"
#include "picdac.h"
#include "piccomparator.h"
#include "picvref.h"
#include "picwdt.h"
#include "piceeprom.h"
#include "picconfigword.h"
#include "picintosc.h"
#include "picsleep.h"

#include "i51core.h"
#include "i51timer.h"
#include "i51usart.h"
#include "i51port.h"

#include "mcs65core.h"

#include "z80core.h"
#include "ula_zx48k.h"

#include "scriptcpu.h"
#include "scriptport.h"
#include "scriptusart.h"
#include "scriptspi.h"
#include "scripttwi.h"
#include "scripttcp.h"
#include "scriptprop.h"
#include "scriptdisplay.h"

#include "utils.h"

QList<DisplayArea*> McuCreator::m_displays; 
bool McuCreator::m_console; 

QString McuCreator::m_core = ""; 
QString McuCreator::m_CompName = ""; 
QString McuCreator::m_basePath = ""; 
QString McuCreator::m_txRegName = ""; 
Mcu*    McuCreator::m_mcuComp = nullptr; 
eMcu*   McuCreator::mcu = nullptr; 
McuTwi* McuCreator::m_twi = nullptr; 
McuSpi* McuCreator::m_spi = nullptr; 
bool    McuCreator::m_newStack; 
QDomElement McuCreator::m_stackEl; 
std::vector<ScriptPerif*> McuCreator::m_scriptPerif; 


int McuCreator::createMcu( Mcu* mcuComp, QString name )
{
    m_CompName = name; 
    m_mcuComp  = mcuComp; 
    m_newStack = false; 
    m_console  = false; 
    m_scriptPerif.clear(); 
    m_displays.clear(); 

    m_twi = nullptr; 
    m_spi = nullptr; 

    mcu = &(mcuComp->m_eMcu); 
    QString dataFile = mcuComp->m_dataFile; 
    m_basePath = QFileInfo( dataFile ).absolutePath(); 
    dataFile   = QFileInfo( dataFile ).fileName(); 

    int error = processFile( dataFile ); 

    if( error == 0 ) m_mcuComp->setupMcu(); 

    return error; 
}

int McuCreator::processFile( QString fileName )
{
    fileName = m_basePath+"/"+fileName; 
    QDomDocument domDoc = fileToDomDoc( fileName, "McuCreator::processFile" ); 
    if( domDoc.isNull() ) return 1; 

    QDomElement root = domDoc.documentElement(); 

    QString tagName = root.tagName(); 

    if( tagName != "mcu" && tagName != "iou" && tagName != "mpu" && tagName != "parts")
    {
        QDomNode node = root.firstChild(); 

        while( !node.isNull() )
        {
            root = node.toElement(); 
            tagName = root.tagName(); 
            if( tagName == "mcu" || tagName == "mpu" || tagName == "iou" || tagName == "parts" ) break; 
            node = node.nextSibling(); 
        }
    }

    if( root.hasAttribute("core") )
    {
        m_core = root.attribute("core"); 

        if( root.hasAttribute("data") )       createDataMem( root.attribute("data").toUInt(0,0) ); 
        if( root.hasAttribute("prog") )       createProgMem( root.attribute("prog").toUInt(0,0) ); 
        if( root.hasAttribute("progword") )   mcu->m_wordSize = root.attribute("progword").toUInt(0,0); 
        if( root.hasAttribute("progpage") )   mcu->m_pgmPage = root.attribute("progpage").toUInt(0,0); 
        if( root.hasAttribute("eeprom") )     createRomMem( root.attribute("eeprom").toUInt(0,0) ); 
        if( root.hasAttribute("inst_cycle") ) mcu->setInstCycle( root.attribute("inst_cycle").toDouble() ); 
        if( root.hasAttribute("cpu_cycle") )  mcu->m_cPerTick = root.attribute("cpu_cycle").toDouble(); 
        if( root.hasAttribute("freq") )       m_mcuComp->m_uiFreq = root.attribute("freq").toDouble(); 
    }

    int error = 0; 
    QDomNode node = root.firstChild(); 
    while( !node.isNull() )
    {
        QDomElement el = node.toElement(); 
        QString   part = el.tagName(); 

        if     ( part == "regblock" )   createRegisters( &el ); 
        else if( part == "datablock" )  createDataBlock( &el ); 
        else if( part == "progblock" )  createProgBlock( &el ); 
        else if( part == "stack" )      { m_stackEl = el;  m_newStack = true;  }
        else if( part == "interrupts" ) createInterrupts( &el ); 
        else if( part == "port" )       createMcuPort( &el ); 
        else if( part == "mcuport" )    createMcuPort( &el ); 
        else if( part == "ioport" )     createIoPort( &el ); 
        else if( part == "timer" )      createTimer( &el ); 
        else if( part == "ocm" )        createOcm( &el ); 
        else if( part == "ccpunit" )    createCcpUnit( &el ); 
        else if( part == "msspunit" )   createMsspUnit( &el ); 
        else if( part == "usart" )      createUsart( &el ); 
        else if( part == "twi" )        createTwi( &el ); 
        else if( part == "spi" )        createSpi( &el ); 
        else if( part == "tcp" )        createTcp( &el ); 
        else if( part == "usi" )        createUsi( &el ); 
        else if( part == "adc" )        createAdc( &el ); 
        else if( part == "dac" )        createDac( &el ); 
        else if( part == "comp" )       createAcomp( &el ); 
        else if( part == "vref" )       createVref( &el ); 
        else if( part == "wdt" )        createWdt( &el ); 
        else if( part == "rom" )        createEeprom( &el ); 
        else if( part == "sleep" )      createSleep( &el ); 
        else if( part == "configwords") createCfgWord( &el ); 
        else if( part == "intosc")      createIntOsc( &el ); 
        else if( part == "display" )    createDisplay( &el ); 
        else if( part == "console" )    m_console = true; 

        else if( part == "include" )
        {
            error = processFile( el.attribute("file") ); 
            if( error ) return error; 
        }
        node = node.nextSibling(); 
    }
    if( root.hasAttribute("core") )
    {
        if     ( m_core == "AVR" )      mcu->m_cpu = new AvrCore( mcu ); 
        else if( m_core == "Pic12" )    mcu->m_cpu = new Pic12Core( mcu ); 
        else if( m_core == "Pic14" )    mcu->m_cpu = new Pic14Core( mcu ); 
        else if( m_core == "Pic14e" )   mcu->m_cpu = new Pic14eCore( mcu ); 
        else if( m_core == "8051" )     mcu->m_cpu = new I51Core( mcu ); 
        else if( m_core == "6502" )     mcu->m_cpu = new Mcs65Cpu( mcu ); 
        else if( m_core == "Z80" )      mcu->m_cpu = new Z80Core( mcu ); 
        else if( m_core == "Z80ULA" )   mcu->m_cpu = new ULA_ZX48k( mcu ); 
        else if( m_core == "scripted" )
        {
            ScriptCpu* cpu = new ScriptCpu( mcu ); 
            mcu->m_cpu = cpu; 
            m_mcuComp->m_scripted = true; 

            //if( root.attribute("linkable") == "true"
            // || root.attribute("linker"  ) == "true" )
                m_mcuComp->setScriptCpu( cpu ); 

            node = root.firstChild(); 
            while( !node.isNull() )
            {
                QDomElement e = node.toElement(); 
                QString  part = e.tagName(); 

                if( part == "propertygroup" )  // Properties in .mcu file
                {
                    SubCircuit* subC = Circuit::self()->getSubcircuit(); 
                    QString group = e.attribute("name"); 

                    QList<ComProperty*> mcuProps; 
                    QList<ComProperty*> subProps; 

                    QDomNode node = e.firstChild(); 
                    while( !node.isNull() )
                    {
                        QDomElement el = node.toElement(); 

                        if( el.tagName() == "property" )
                        {
                            QString name  = el.attribute("name"); 
                            QString label = el.attribute("label"); 
                            QString type  = el.attribute("type"); 
                            QString unit  = el.attribute("unit"); 

                            if( label.isEmpty() ) label = name; 

                            ComProperty* p = cpu->addProperty( name, label, type, unit ); 

                            if( el.attribute("add") == "true" ) subProps.append( p ); 
                            else                                mcuProps.append( p ); 
                        }
                        node = node.nextSibling(); 
                    }
                    propGroup* pg = m_mcuComp->getPropGroup( group ); 
                    if( pg ){
                        for( ComProperty* p : mcuProps ) m_mcuComp->addProperty( group, p ); 
                    }
                    else m_mcuComp->addPropGroup( {group, mcuProps, 0} ); 

                    if( subC ){
                        pg = subC->getPropGroup( group ); 
                        if( pg ){
                            for( ComProperty* p : subProps ) subC->addProperty( group, p/*, false*/ );  // If false properties are saved but not set at circuit open
                        }
                        else subC->addPropGroup( {group, subProps, 0}/*, false*/ ); 
                    }
                }
                node = node.nextSibling(); 
            }
            cpu->setPeriferals( m_scriptPerif ); 

            cpu->setScriptFile( m_basePath+"/"+root.attribute("script") ); 
        }
        else mcu->m_cpu = new Mcu8bits( mcu ); 

        if( m_displays.size() )
        {
            mcu->m_cpu->createWatcher(); 
            for( DisplayArea* display : m_displays )
            {
                mcu->m_cpu->m_display = display; 
                mcu->m_cpu->getWatcher()->addWidget( display ); 
            }
        }
        if( m_console )
        {
            mcu->m_cpu->createWatcher(); 
            mcu->m_cpu->getWatcher()->addConsole(); 
        }
        if( m_newStack ) createStack( &m_stackEl ); 
    }

    if( root.hasAttribute("clkpin") )
    {
        mcu->m_clkPin = mcu->getIoPin( root.attribute("clkpin") ); 
    }
    return 0; 
}

void McuCreator::createCfgWord( QDomElement* e )
{
    QString name = e->attribute("name"); 
    QString type = e->attribute("type"); 

    ConfigWord* cfgWord = nullptr; 
    if( m_core.startsWith("Pic14") ) cfgWord = PicConfigWord::createCfgWord( mcu, name, type ); 
    if( !cfgWord ) return; 

    mcu->m_cfgWord = cfgWord; 

    QDomNode node = e->firstChild(); 
    while( !node.isNull() )
    {
        QDomElement el = node.toElement(); 
        if( el.tagName() == "word" )
        {
            //QString name = el.attribute("name"); 
            uint16_t addr  = el.attribute("address").toUInt( nullptr, 16 ); 
            uint16_t value = el.attribute("value").toUInt( nullptr, 16 ); 
            cfgWord->m_cfgWords.insert( addr, value ); 
        }
        node = node.nextSibling(); 
    }
}

void McuCreator::createProgMem( uint32_t size )
{
    mcu->m_flashSize = size; 
    if     ( m_core == "Pic12" )  mcu->m_progMem.resize( size, 0x0FFF ); 
    else if( m_core == "Pic14" )  mcu->m_progMem.resize( size, 0x3FFF ); 
    else if( m_core == "Pic14e" ) mcu->m_progMem.resize( size, 0x3FFF ); 
    else                          mcu->m_progMem.resize( size, 0xFFFF ); 
}

void McuCreator::createDataMem( uint32_t size )
{
    mcu->m_ramSize = size; 
    mcu->m_dataMem.resize( size, 0 ); 
    mcu->m_addrMap.resize( size, 0xFFFF );  // Not Maped values = 0xFFFF -> don't exist
}

void McuCreator::createRomMem( uint32_t size )
{
    if( size == 0 ) return; 

    mcu->m_romSize = size; 
    mcu->m_eeprom.resize( size ); 
    mcu->m_eeprom.fill( 0xFF ); 
}

void McuCreator::createEeprom( QDomElement* e )
{
    McuEeprom* eeprom = nullptr; 
    QString eepromName = e->attribute("name"); 

    if     ( m_core == "AVR" )    eeprom = new AvrEeprom( mcu, eepromName ); 
    else if( m_core == "Pic14" )  eeprom = new PicEeprom( mcu, eepromName ); 
    else if( m_core == "Pic14e" ) eeprom = new PicEeprom( mcu, eepromName ); 
    else return; 

    mcu->m_modules.emplace_back( eeprom ); 

    setConfigRegs( e, eeprom ); 

    if( e->hasAttribute("dataregs") ){
        QStringList regs = e->attribute("dataregs").split(","); 
        eeprom->m_dataReg = mcu->getReg( regs.value(0) ); 
    }

    if( e->hasAttribute("addressreg") )
    {
        QStringList regs = e->attribute("addressreg").split(","); 
        QString lowByte  = regs.value(0); 
        QString highByte = regs.value(1); 

        if( !lowByte.isEmpty() ){
            eeprom->m_addressL = mcu->getReg( lowByte ); 
            watchRegNames( lowByte, R_WRITE, eeprom, &McuEeprom::addrWriteL, mcu ); 
        }
        if( !highByte.isEmpty() ){
            eeprom->m_addressH = mcu->getReg( highByte ); 
            watchRegNames( highByte, R_WRITE, eeprom, &McuEeprom::addrWriteH, mcu ); 
    }   }
    setInterrupt( e->attribute("interrupt"), eeprom ); 
}

void McuCreator::createDataBlock( QDomElement* d )
{
    uint16_t datStart = d->attribute("start").toUInt(0,0); 
    uint16_t datEnd   = d->attribute("end").toUInt(0,0); 
    uint16_t mapTo    = datStart; 

    if( datEnd >= mcu->m_ramSize )
    {
        qDebug() << "McuCreator::createDataBlock  ERROR creating DataBlock"; 
        qDebug() << "dataMemSize  = " << mcu->m_ramSize; 
        qDebug() << "dataBlockEnd = " << datEnd; 
        return; 
    }
    if( d->hasAttribute("mapto") ) mapTo = d->attribute("mapto").toUInt(0,0); 

    for( int i=datStart;  i<=datEnd;  ++i )
    {
        if( mcu->m_addrMap.at(i) == 0xFFFF ) mcu->m_addrMap.at(i) = mapTo;  // Don't overwrite previous values
        mapTo++; 
    }
    getRegisters( d ); 
}

void McuCreator::createRegisters( QDomElement* e )
{
    uint16_t regStart = e->attribute("start").toUInt(0,0); 
    uint16_t regEnd   = e->attribute("end").toUInt(0,0); 
    uint16_t offset   = e->attribute("offset").toUInt(0,0); 

    if( regEnd >= mcu->m_ramSize )
    {
        qDebug() << "McuCreator::createRegisters  ERROR creating Registers"; 
        qDebug() << "dataMemSize  = " << mcu->m_ramSize; 
        qDebug() << "RegistersEnd = " << regEnd; 
        return; 
    }
    if( regStart < mcu->m_regStart ) mcu->m_regStart = regStart; 
    if( regEnd   > mcu->m_regEnd )
    {
        mcu->m_regEnd = regEnd; 
        mcu->m_regMask.resize( regEnd+1, 0xFF ); 
    }
    for( int i=regStart;  i<=regEnd;  ++i )
        if( mcu->m_addrMap.at(i) == 0xFFFF ) mcu->m_addrMap.at(i) = i;  // Don't overwrite previous values

    getRegisters( e, offset ); 
}

void McuCreator::getRegisters( QDomElement* e, uint16_t offset )
{
    QString stReg; 
    if( e->hasAttribute("streg") ) stReg = e->attribute("streg"); 

    QDomNode node = e->firstChild(); 
    while( !node.isNull() ) // Create Registers
    {
        QDomElement el = node.toElement(); 

        if( el.tagName() == "register" )
        {
            QString  regName = el.attribute("name"); 
            uint16_t regAddr = el.attribute("addr").toUInt(0,0)+offset; 

            if( regAddr >= mcu->m_ramSize )
            {
                qDebug() << "McuCreator::getRegisters  ERROR creating Register"<< regName ; 
                qDebug() << "dataMemSize  = " << mcu->m_ramSize; 
                qDebug() << "Register Address = " << regAddr<<"\n"; 
            }
            else{
                uint8_t resetVal = el.attribute("reset").toUInt(0,2); 
                QString    wMask = el.attribute("mask"); 
                QString     bits = el.attribute("bits"); 

                if( !wMask.isEmpty() )
                {
                    if( regAddr >= mcu->m_regMask.size() )
                    {
                        qDebug() << "McuCreator::getRegisters  ERROR creating Register Mask"<< regName ; 
                        qDebug() << "regMask Size  = " << mcu->m_regMask.size(); 
                        qDebug() << "Register Address = " << regAddr<<"\n"; 
                    }
                    else mcu->m_regMask.at(regAddr) = wMask.toUInt(0,2); 
                }
                mcu->m_addrMap.at(regAddr) = regAddr; 

                regInfo_t regInfo = { regAddr, resetVal/*, writeMask*/ }; 
                mcu->m_regInfo.insert( regName, regInfo ); 

                if( !bits.isEmpty() )                    // Create bitMasks
                {
                    QString bitName; 
                    QStringList bitList = bits.split(","); 
                    for( int i=0;  i<bitList.size();  ++i )
                    {
                        bitName = bitList.value( i ); 
                        if( bitName == "0" ) continue; 

                        for( QString alias : bitName.split("|") ) // Bit name variations: alias first used bit name
                        {                                         // Example tiny WDTCR.WDTIE is 328 WDTCSR.WDIE
                            mcu->m_bitMasks.insert( alias, 1<<i ); 
                            mcu->m_bitRegs.insert( alias, regAddr ); 
                    }   }
                    if( !stReg.isEmpty() && ( regName == stReg ) ) // Status Register
                    {
                        mcu->m_sregAddr = regAddr; 
                        mcu->setStatusBits( bitList ); 
                    }
                }
            }
        }
        else if( el.tagName() == "mapped" )
        {
            uint16_t regAddr = el.attribute("addr").toUInt(0,0)+offset; 
            uint16_t mapTo   = el.attribute("mapto").toUInt(0,0); 
            mcu->m_addrMap.at(regAddr) = mapTo; 
        }
        node = node.nextSibling(); 
}   }

void McuCreator::createProgBlock( QDomElement* p )
{
    QDomNode node = p->firstChild(); 
    while( !node.isNull() )
    {
        QDomElement el = node.toElement(); 
        if( el.tagName() == "progval" )
        {
            uint16_t addr  = el.attribute("addr").toUInt(0,0); 
            uint16_t value = el.attribute("value").toUInt(0,0); 

            if( addr >= mcu->m_flashSize )
            {
                qDebug() << "McuCreator::createProgBlock  ERROR writing Program Memory"; 
                qDebug() << "Address:" << addr << "> PMG End:" << mcu->m_flashSize; 
            }
            else mcu->setFlashValue( addr, value ); 
        }
        node = node.nextSibling(); 
}   }

void McuCreator::createIntOsc( QDomElement* p )
{
    QString name = p->attribute("name"); 
    QString type = p->attribute("type"); 

    McuIntOsc* intOsc = nullptr; 
    if( m_core.startsWith("Pic14") ) intOsc = PicIntOsc::createIntOsc( mcu, name, type ); 
    else if( m_core == "AVR")        intOsc = new AvrIntOsc( mcu, "intOsc"); 
    else                             intOsc = new McuIntOsc( mcu, "intOsc"); 

    mcu->m_modules.emplace_back( intOsc ); 
    mcu->m_intOsc = intOsc; 

    setConfigRegs( p, intOsc ); 

    if( p->hasAttribute("clockpins") )
    {
        QStringList pins = p->attribute("clockpins").split(","); 
        for( int i=0;  i<pins.size();  ++i )
            mcu->m_intOsc->setPin( i, mcu->getMcuPin( pins.value(i) ) ); 
    }
    if( p->hasAttribute("clockoutpin") )
    {
        mcu->m_intOsc->setPin( 2, mcu->getMcuPin( p->attribute("clockoutpin") ) ); 
    }    
}

void McuCreator::createIoPort( QDomElement* p )
{
    QString name = p->attribute("name"); 

    IoPort* port = new IoPort( name ); 
    mcu->m_ioPorts.insert( name, port ); 

    port->createPins( m_mcuComp, p->attribute("pins"), 0xFFFFFFFF ); 
}

void McuCreator::createMcuPort( QDomElement* p )
{
    QString name = p->attribute("name"); 

    McuPort* port; 
    if     ( m_core == "AVR" )    port = new AvrPort( mcu, name ); 
    else if( m_core == "Pic12" )  port = new PicPort( mcu, name ); 
    else if( m_core == "Pic14" )  port = new PicPort( mcu, name ); 
    else if( m_core == "Pic14e" ) port = new PicPort( mcu, name ); 
    else if( m_core == "8051" )   port = new I51Port( mcu, name ); 
    else{
        if( p->hasAttribute("type") && ( p->attribute("type") == "scripted" ))
        {
            port = new ScriptPort( mcu, name ); 
            ScriptPort* sPort = (ScriptPort*)port; 
            sPort->setScriptFile( m_basePath+"/"+p->attribute("script")); 
        }
        else port = new McuPort( mcu, name ); 
    }
    mcu->m_mcuPorts.insert( name, port ); 
    mcu->m_modules.emplace_back( port ); 

    uint32_t pinMask=0xFFFFFFFF; 
    if( p->hasAttribute("pinmask") ) pinMask = p->attribute("pinmask").toUInt( 0, 2 ); 
    port->createPins( m_mcuComp, p->attribute("pins"), pinMask ); 

    setConfigRegs( p, port ); 

    if( p->hasAttribute("resetpin") )
        m_mcuComp->m_portRstPin = port->getPin( p->attribute("resetpin") ); 

    QString Oreg = ""; 
    if( p->hasAttribute( "outreg" ) )
    {
        QStringList outRegList = p->attribute( "outreg" ).split(","); 
        Oreg = outRegList.first(); 
        for( QString outReg : outRegList )
        {
            uint16_t addr = mcu->getRegAddress( outReg ); 
            port->m_outAddr = addr; 
            port->m_outReg  = mcu->getReg( outReg ); 
            watchRegNames( outReg, R_WRITE, port, &McuPort::outChanged, mcu ); 
    }   }
    if( p->hasAttribute( "inreg" ) ) // Connect to PORT In Register
    {
        QString inReg = p->attribute( "inreg" ); 
        uint16_t inAddr = mcu->getRegAddress( inReg ); 
        port->m_inAddr = inAddr; 
        port->m_inReg  = mcu->getReg( inReg ); 
        if( m_core == "AVR" ) watchRegister( inAddr, R_WRITE, (AvrPort*)port, &AvrPort::pinRegChanged, mcu ); 
    }
    else if( !Oreg.isEmpty() ) watchRegNames( Oreg, R_READ, port, &McuPort::readPort, mcu );  // No Input register, read pin states

    if( p->hasAttribute( "dirreg" ) ) // Connect to PORT Dir Register
    {
        QString dirreg = p->attribute( "dirreg" ); 
        if( dirreg.startsWith("!") )
        {
            port->m_dirInv = true; 
            dirreg.remove( 0, 1 ); 
        }
        uint16_t addr = mcu->getRegAddress( dirreg ); 
        port->m_dirAddr = addr; 
        port->m_dirReg  = mcu->getReg( dirreg ); 
        watchRegNames( dirreg, R_WRITE, port, &McuPort::dirChanged, mcu ); 
    }
    if( p->hasAttribute("outmask") ) // Permanent Outputs
    {
        uint outMask = p->attribute("outmask").toUInt( 0, 2 ); 
        for( int i=0;  i<port->m_numPins;  ++i )
            port->m_pins[i]->m_outMask = outMask & 1<<i; 
    }
    if( p->hasAttribute("inpmask") ) // Permanent Inputs
    {
        uint inpMask = p->attribute("inpmask").toUInt( 0, 2 ); 
        for( int i=0;  i<port->m_numPins;  ++i )
            port->m_pins[i]->m_inpMask = inpMask & 1<<i; 
    }
    if( p->hasAttribute("pullups") )
    {
        QString pullups = p->attribute( "pullups" ); 
        bool ok = false; 
        uint pullup = pullups.toUInt( &ok, 2 ); 
        if( ok ){
            for( int i=0;  i<port->m_numPins;  ++i )          // Permanent Pullups
                port->m_pins[i]->m_puMask = pullup & 1<<i; 
        }else{
            if( mcu->regExist( pullups ) )
                watchRegNames( pullups, R_WRITE, port, &McuPort::setPullups, mcu ); 
            else{
                if( pullups.startsWith("!") ){
                    pullups = pullups.remove( 0,1 ); 
                    watchBitNames( pullups, R_WRITE, port, &McuPort::clearAllPullups, mcu ); 
                }
                else watchBitNames( pullups, R_WRITE, port, &McuPort::setAllPullups, mcu ); 
            }
        }
    }
    if( p->hasAttribute("opencol") ) // OPen Drain
    {
        uint opencol = p->attribute("opencol").toUInt( 0, 2 ); 
        for( int i=0;  i<port->m_numPins;  ++i )
            if( opencol & 1<<i ) port->m_pins[i]->m_openColl = true; 
    }
    QDomNode node = p->firstChild(); 
    while( !node.isNull() )
    {
        QDomElement el = node.toElement(); 

        if( el.tagName() == "interrupt" )
        {
            setInterrupt( el.attribute("name"), port );   // Pin change interrupt

            if( el.hasAttribute("mask") )
            {
                QString mask = el.attribute( "mask" ); 
                bool ok = false; 
                int bits = mask.toInt( &ok,2 ); 
                port->rstIntMask( !ok ); 
                if( ok ) port->setIntMask( bits );  // Hardcode Interrupt to Pin
                else                               // Interrupt controlled by register
                {
                    watchRegNames( mask, R_WRITE, port, &McuPort::intChanged, mcu ); 
                }
            }
            else if(  el.hasAttribute("bitmask") )
            {
                QString bitNames = el.attribute( "bitmask" ); 
                port->m_intBits = getRegBits( bitNames, mcu ); 
                watchBitNames( bitNames, R_WRITE, port, &McuPort::intChanged, mcu ); 
            }
        }
        else if( el.tagName() == "extint" )
        {
            QString intName = el.attribute("name"); 
            Interrupt* inte = mcu->m_interrupts.m_intList.value( intName ); 

            McuPin* pin = port->getPin( el.attribute("pin") ); 
            if( pin ) pin->m_extInt = inte; 

            if( el.hasAttribute("configbits") )
            {
                QString configBits = el.attribute("configbits"); 
                watchBitNames( configBits, R_WRITE, pin, &McuPin::ConfExtInt, mcu ); 
                if( pin ) pin->m_extIntBits = getRegBits( configBits, mcu ); 
        }   }
        node = node.nextSibling(); 
}   }

void McuCreator::createTimer( QDomElement* t )
{
    McuTimer* timer = nullptr; 
    QString timerName = t->attribute("name"); 
    int type = t->attribute("type").toInt(); 

    if     ( m_core == "8051" )   timer = new I51Timer( mcu, timerName ); 
    else if( m_core == "AVR" )    timer = AvrTimer::createTimer( mcu, timerName, type ); 
    else if( m_core == "Pic12" )  timer = PicTimer::createTimer( mcu, timerName, type ); 
    else if( m_core == "Pic14" )  timer = PicTimer::createTimer( mcu, timerName, type ); 
    else if( m_core == "Pic14e" ) timer = PicTimer::createTimer( mcu, timerName, type ); 

    if( !timer ) { qDebug() << "Error creating Timer"<< timerName;  return;  }

    mcu->m_timerList.insert( timerName, timer ); 
    mcu->m_modules.emplace_back( timer ); 

    setConfigRegs( t, timer ); 

    if( t->hasAttribute("counter") )
    {
        QStringList regs = t->attribute("counter").split(","); 
        QString lowByte  = regs.value(0); 
        QString highByte = regs.value(1); 

        if( !lowByte.isEmpty() ){
            timer->m_countL = mcu->getReg( lowByte ); 
            watchRegNames( lowByte, R_WRITE, timer, &McuTimer::countWriteL, mcu ); 
            watchRegNames( lowByte, R_READ,  timer, &McuTimer::updtCount, mcu ); 
        }
        if( !highByte.isEmpty() ){
            timer->m_countH = mcu->getReg( highByte ); 
            watchRegNames( highByte, R_WRITE, timer, &McuTimer::countWriteH, mcu );   // Low byte triggers read/write operations
            watchRegNames( highByte, R_READ,  timer, &McuTimer::updtCount, mcu ); 
    }   }
    if( t->hasAttribute("enable") )
    {
        QString enable = t->attribute("enable"); 
        watchBitNames( enable, R_WRITE, timer, &McuTimer::enable, mcu ); 
    }

    if( t->hasAttribute("clockpin") )
    {
        QStringList pinList = t->attribute("clockpin").split(","); 
        timer->setClockPins( pinList ); 
    }

    QString topReg0 = ""; 
    if( t->hasAttribute("topreg0") ) /// Still done in AvrTimer16bit
    {
        topReg0 = t->attribute("topreg0"); 
        QStringList list = topReg0.split(","); 

        timer->m_topReg0L = mcu->getReg( list.value(0) ); 
        watchRegNames( topReg0, R_WRITE, timer, &McuTimer::topReg0Changed, mcu ); 

        if( list.size() > 1 ) timer->m_topReg0H = mcu->getReg( list.value(1) ); 
    }
    setInterrupt( t->attribute("interrupt"), timer ); 
    setPrescalers( t->attribute("prescalers"), timer ); 
    if( t->hasAttribute("prselect") ) timer->m_prSelBits = getRegBits( t->attribute("prselect"), mcu ); 

    QDomNode node = t->firstChild(); 
    while( !node.isNull() )
    {
        QDomElement el = node.toElement(); 

        if( el.tagName() == "ocunit" )
        {
            QString ocName = el.attribute("name"); 
            McuOcUnit* ocUnit = nullptr; 
            if     ( m_core == "AVR" ) ocUnit = new AvrOcUnit( mcu, ocName ); 
            //else if( m_core == "Pic14" ) ocUnit = new PicOcUnit( mcu, el.attribute("name") ); 
            if( !ocUnit ) { node = node.nextSibling();  continue;  }

            timer->addOcUnit( ocUnit ); 
            ocUnit->m_timer = timer; 
            ocUnit->m_ocPin = mcu->getMcuPin( el.attribute("pin") ); 

            QStringList regs = el.attribute("ocreg").split(","); 
            QString lowByte = regs.value(0); 
            QString highByte =regs.value(1); 

            if( !lowByte.isEmpty() ){
                uint8_t* ocRegL = mcu->getReg( lowByte ); 
                ocUnit->m_ocRegL = ocRegL; 
                if( topReg0 == "" && ocName.endsWith("A") ) // OCRA managed in Timer
                {
                    timer->m_topReg0L = ocRegL; 
                    watchRegNames( lowByte, R_WRITE, timer, &McuTimer::topReg0Changed, mcu ); 
                }
                else watchRegNames( lowByte, R_WRITE, ocUnit, &McuOcUnit::ocrWriteL, mcu ); 
            }
            if( !highByte.isEmpty() ){
                uint8_t* ocRegH = mcu->getReg( highByte ); 
                ocUnit->m_ocRegH = ocRegH; 
                if( ocName.endsWith("A") ) timer->m_topReg0H = ocRegH; 
                /// Low byte triggers red/write operations
                watchRegNames( highByte, R_WRITE, ocUnit, &McuOcUnit::ocrWriteH, mcu ); 
            }
            if( el.hasAttribute("bits") ) // This doesn't watch register, configure must be called from Timer
            {
                ocUnit->m_configBitsA = getRegBits( el.attribute("bits"), mcu ); 
            }
            else setConfigRegs( &el, ocUnit ); 

            setInterrupt( el.attribute("interrupt"), ocUnit ); 
        }
        else if( el.tagName() == "icunit" )
        {
            QString icName = el.attribute("name"); 
            McuIcUnit* icUnit = nullptr; 
            if( m_core == "AVR" ) icUnit = new AvrIcUnit( mcu, icName ); 
            if( !icUnit ) { node = node.nextSibling();  continue;  }

            timer->m_ICunit = icUnit; 
            icUnit->m_timer = timer; 
            icUnit->m_icPin = mcu->getMcuPin( el.attribute("pin") ); 

            QStringList regs = el.attribute("icreg").split(","); 
            if( !regs.isEmpty() ) icUnit-> m_icRegL = mcu->getReg( regs.value(0) ); 
            if( !regs.isEmpty() ) icUnit-> m_icRegH = mcu->getReg( regs.value(1) ); 

            if( el.hasAttribute("bits") )
            {
                QString configBits = el.attribute("bits"); 
                watchBitNames( configBits, R_WRITE, icUnit, &McuIcUnit::configure, mcu ); 
            }
            setInterrupt( el.attribute("interrupt"), icUnit ); 
        }
        node = node.nextSibling(); 
}   }

void McuCreator::createOcm( QDomElement* e )
{
    McuOcm* ocm = nullptr; 
    QString name = e->attribute("name"); 

    if( m_core == "AVR" ) ocm = new AvrOcm( mcu, name ); 
    if( !ocm ) return; 

    mcu->m_modules.emplace_back( ocm ); 
    setConfigRegs( e, ocm ); 

    QStringList ocunits = e->attribute("ocunits").split(","); 
    for( int i=0;  i<2;  i++ )
    {
        QString ocunit = ocunits.takeFirst(); 
        McuTimer* timer = mcu->getTimer("TIMER"+ocunit.mid( 2, 1) ); 

        McuOcUnit* ocUnit = timer->getOcUnit( ocunit ); 
        ocUnit->m_ocm = ocm; 
        if( i == 0 ) ocm->m_OC1 = ocUnit; 
        else         ocm->m_OC2 = ocUnit; 
    }
}

void McuCreator::createCcpUnit( QDomElement* c )
{
    PicCcpUnit* ccpUnit = nullptr; 
    if     ( m_core == "Pic14" ) ccpUnit = new PicCcpUnit( mcu, c->attribute("name"), c->attribute("type").toInt() ); 
    else if( m_core == "Pic14e") ccpUnit = new PicCcpUnit( mcu, c->attribute("name"), c->attribute("type").toInt() ); 
    if( !ccpUnit ) return; 

    mcu->m_modules.emplace_back( ccpUnit ); 

    ccpUnit->setPin( mcu->getMcuPin( c->attribute("pin") ) ); 

    QStringList regs = c->attribute("ccpreg").split(","); 
    QString lowByte  = regs.value(0); 
    QString highByte = regs.value(1); 

    if( !lowByte.isEmpty() ){
        uint8_t* regL = mcu->getReg( lowByte ); 
        ccpUnit->m_ccpRegL = regL; 
        ccpUnit->m_capUnit->m_icRegL = regL; 
        watchRegNames( lowByte, R_WRITE, ccpUnit, &PicCcpUnit::ccprWriteL, mcu ); 
    }
    if( !highByte.isEmpty() ){
        uint8_t* regH = mcu->getReg( highByte ); 
        ccpUnit->m_ccpRegH = regH; 
        ccpUnit->m_capUnit->m_icRegH = regH; 
        watchRegNames( highByte, R_WRITE, ccpUnit, &PicCcpUnit::ccprWriteH, mcu ); 
    }
    setConfigRegs( c, ccpUnit ); 
    setInterrupt( c->attribute("interrupt"), ccpUnit ); 
}

void McuCreator::createMsspUnit( QDomElement* c )
{
    PicMssp* msspUnit = new PicMssp(  mcu, c->attribute("name"), c->attribute("type").toInt() ); 

    mcu->m_modules.emplace_back( msspUnit ); 
    setConfigRegs( c, msspUnit ); 

    QDomNode node = c->firstChild(); 
    while( !node.isNull() )
    {
        QDomElement el = node.toElement(); 
        if     ( el.tagName() == "twi") createTwi( &el ); 
        else if( el.tagName() == "spi") createSpi( &el ); 
        node = node.nextSibling(); 
    }
    msspUnit->m_twiUnit = static_cast<PicTwi*>( m_twi ); 
    msspUnit->m_spiUnit = static_cast<PicSpi*>( m_spi ); 
}

void McuCreator::createUsart( QDomElement* u )
{
    QString name = u->attribute( "name" ); 
    int   number = u->attribute( "number" ).toInt(); 

    QString core = m_core; 
    if( u->hasAttribute("core") ) core = u->attribute("core"); 

    McuUsart* usartM; 
    if     ( core == "8051" )    usartM = new I51Usart( mcu, name, number ); 
    else if( core == "AVR"  )    usartM = new AvrUsart( mcu, name, number ); 
    else if( core == "Pic14")    usartM = new PicUsart( mcu, name, number ); 
    else if( core == "Pic14e")   usartM = new PicUsart( mcu, name, number ); 
    else if( core == "scripted"){
        ScriptUsart* su = new ScriptUsart( mcu, name, number ); 
        usartM = su; 
        m_scriptPerif.push_back( su ); 
    }
    else return; 

    mcu->m_transModules.emplace_back( usartM ); 
    mcu->m_modules.emplace_back( usartM ); 

    setConfigRegs( u, usartM ); 
    setInterrupt( u->attribute("interrupt"), usartM ); 

    QDomNode node = u->firstChild(); 
    while( !node.isNull() )
    {
        QDomElement el = node.toElement(); 

        if( el.tagName() == "trunit" )
        {
            UartTR* trUnit = nullptr; 
            QString type = el.attribute( "type" ); 
            if( type == "tx" )
            {
                trUnit = usartM->m_sender; 
                m_txRegName = el.attribute( "register" ); 
                watchRegNames( m_txRegName, R_WRITE, usartM, &McuUsart::sendByte, mcu ); 
            }
            else if( type == "rx" )
            {
                trUnit = usartM->m_receiver; 
                QString regName = el.attribute( "register" ); 
                if( regName.isEmpty() ) regName = m_txRegName;  // Tx and rx using the same register
                watchRegNames( regName, R_READ, usartM, &McuUsart::readByte, mcu ); 
            }
            if( trUnit )
            {
                setConfigRegs( &el, trUnit ); 

                QStringList pinNames = el.attribute( "pin" ).split(","); 
                QList<IoPin*> pinList; 
                for( QString pinName : pinNames ) pinList.append( mcu->getIoPin( pinName ) ); 
                trUnit->setPins( pinList ); 

                if( el.hasAttribute("enable") )
                {
                    QString enable = el.attribute( "enable" ); 
                    watchBitNames( enable, R_WRITE, trUnit, &UartTR::enable, mcu ); 
                }
                setInterrupt( el.attribute("interrupt"), trUnit ); 
            }
        }
        node = node.nextSibling(); 
}   }

void McuCreator::createAdc( QDomElement* e )
{
    McuAdc* adc = nullptr; 
    QString name = e->attribute( "name" ); 
    int type = e->attribute("type").toInt(); 

    if     ( m_core == "AVR" )   adc = AvrAdc::createAdc( mcu, name, type ); 
    else if( m_core == "Pic14")  adc = PicAdc::createAdc( mcu, name, type ); 
    else if( m_core == "Pic14e") adc = PicAdc::createAdc( mcu, name, type ); 
    if( !adc ) return; 

    mcu->m_modules.emplace_back( adc ); 

    setConfigRegs( e, adc ); 

    if( e->hasAttribute("bits") )
    {
        bool ok = false; 
        int bits = e->attribute("bits").toInt( &ok ); 
        if( ok ) adc->m_maxValue = pow( 2, bits )-1; 
    }
    if( e->hasAttribute("dataregs") )
    {
        QStringList regs = e->attribute("dataregs").split(","); 
        QString lowByte  = regs.value(0); 
        QString highByte = regs.value(1); 

        if( !lowByte.isEmpty() )  adc->m_ADCL = mcu->getReg( lowByte ); 
        if( !highByte.isEmpty() ) adc->m_ADCH = mcu->getReg( highByte ); 
    }
    if( e->hasAttribute("multiplex") )
    {
        QString configRegs = e->attribute("multiplex"); 
        watchRegNames( configRegs, R_WRITE, adc, &McuAdc::setChannel, mcu ); 
    }
    setInterrupt( e->attribute("interrupt"), adc ); 
    setPrescalers( e->attribute("prescalers"), adc ); 

    QStringList pins = e->attribute("adcpins").remove(" ").split(","); 
    for( QString pinName : pins )
    {
        McuPin* pin = mcu->getMcuPin( pinName ); 
        if( pin ) adc->m_adcPin.emplace_back( pin ); 
    }
    if( e->hasAttribute("vrefpins") )
    {
        pins = e->attribute("vrefpins").remove(" ").split(","); 
        for( QString pinName : pins )
        {
            McuPin* pin = mcu->getMcuPin( pinName ); 
            if( pin ) adc->m_refPin.emplace_back( pin ); 
    }   }
}

void McuCreator::createDac( QDomElement* e )
{
    McuDac* dac = nullptr; 
    QString name = e->attribute( "name" ); 

    if( m_core == "Pic14e") dac = new PicDac( mcu, name ); 
    else return; 

    mcu->m_modules.emplace_back( dac ); 

    setConfigRegs( e, dac ); 

    if( e->hasAttribute("dacreg") )
    {
        QString dacReg = e->attribute("dacreg"); 
        dac->m_dacReg = mcu->getReg( dacReg ); 
        watchRegNames( dacReg, R_WRITE, dac, &McuDac::outRegChanged, mcu ); 
    }
    if( e->hasAttribute("pins") )
    {
        QStringList pins = e->attribute("pins").remove(" ").split(","); 
        for( QString pinName : pins )
        {
            McuPin* pin = mcu->getMcuPin( pinName ); 
            if( pin ) dac->m_pins.emplace_back( pin ); 
    }   }
}

void McuCreator::createAcomp( QDomElement* e )
{
    McuComp* comp = nullptr; 
    QString name = e->attribute( "name" ); 
    int type = e->attribute("type").toInt(); 

    if     ( m_core == "AVR" )    comp = new AvrComp( mcu, name ); 
    else if( m_core == "Pic12" )  comp = PicComp::createComparator( mcu, name, type ); 
    else if( m_core == "Pic14" )  comp = PicComp::createComparator( mcu, name, type ); 
    else if( m_core == "Pic14e" ) comp = PicComp::createComparator( mcu, name, type ); 
    if( !comp ) return; 

    mcu->m_modules.emplace_back( comp ); 
    mcu->m_comparator = comp; 
    setConfigRegs( e, comp ); 

    QStringList pins = e->attribute( "pins" ).split(","); 
    for( QString pinName : pins )
    {
        McuPin* pin = mcu->getMcuPin( pinName ); 
        if( pin ) comp->m_pins.emplace_back( pin ); 
    }
    setInterrupt( e->attribute("interrupt"), comp ); 
}

void McuCreator::createVref( QDomElement* e )
{
    QString name = e->attribute( "name" ); 
    McuVref* vref = nullptr; 
    if     ( m_core == "Pic14" )  vref = new PicVref( mcu, name ); 
    else if( m_core == "Pic14e" ) vref = new PicVrefE( mcu, name ); 
    if( !vref ) return; 

    mcu->m_modules.emplace_back( vref ); 
    mcu->m_vrefModule = vref; 

    setConfigRegs( e, vref ); 

    if( e->hasAttribute("pinout") )
        vref->m_pinOut = mcu->getMcuPin( e->attribute("pinout") ); 
}

void McuCreator::createTwi( QDomElement* e )
{
    QString name = e->attribute( "name" ); 

    if     ( m_core == "AVR" )  m_twi = new AvrTwi( mcu, name ); 
    else if( m_core == "Pic14") m_twi = new PicTwi( mcu, name ); 
    else if( m_core == "scripted"){
        ScriptTwi* twi = new ScriptTwi( mcu, name ); 
        m_twi = twi; 
        m_scriptPerif.push_back( twi ); 
    }
    else return; 

    mcu->m_modules.emplace_back( m_twi ); 
    mcu->m_transModules.emplace_back( m_twi ); 

    setConfigRegs( e, m_twi ); 

    if( e->hasAttribute("dataregs") )
    {
        QString dataReg = e->attribute("dataregs"); 
        m_twi->m_dataReg = mcu->getReg( dataReg ); 
        watchRegNames( dataReg, R_WRITE, m_twi, &McuTwi::writeTwiReg, mcu ); 
        if( m_core == "Pic14")
            watchRegNames( dataReg, R_READ, m_twi, &McuTwi::readTwiReg, mcu );  //
    }
    if( e->hasAttribute("addressreg") )
    {
        QString addrReg = e->attribute("addressreg"); 
        m_twi->m_addrReg = mcu->getReg( addrReg ); 
        watchRegNames( addrReg, R_WRITE, m_twi, &McuTwi::writeAddrReg, mcu ); 
    }
    if( e->hasAttribute("statusreg") )
    {
        QString statReg = e->attribute("statusreg"); 
        m_twi->m_statReg = mcu->getReg( statReg ); 
        watchRegNames( statReg, R_WRITE, m_twi, &McuTwi::writeStatus, mcu ); 
    }
    setInterrupt( e->attribute("interrupt"), m_twi ); 
    setPrescalers( e->attribute("prescalers"), m_twi ); 

    QStringList pins = e->attribute("pins").remove(" ").split(","); 
    m_twi->setSdaPin( mcu->getIoPin( pins.value(0) ) ); 
    m_twi->setSclPin( mcu->getIoPin( pins.value(1) ) ); 
}

void McuCreator::createSpi( QDomElement* e )
{
    QString name = e->attribute( "name" ); 

    if     ( m_core == "AVR" )  m_spi = new AvrSpi( mcu, name ); 
    else if( m_core == "Pic14") m_spi = new PicSpi( mcu, name ); 
    else if( m_core == "scripted"){
        ScriptSpi* su = new ScriptSpi( mcu, name ); 
        m_spi = su; 
        m_scriptPerif.push_back( su ); 
    }
    else return; 

    mcu->m_modules.emplace_back( m_spi ); 
    mcu->m_transModules.emplace_back( m_spi ); 

    setConfigRegs( e, m_spi ); 

    if( e->hasAttribute("dataregs") )
    {
        QString dataReg = e->attribute("dataregs"); 
        m_spi->m_dataReg = mcu->getReg( dataReg ); 
        watchRegNames( dataReg, R_WRITE, m_spi, &McuSpi::writeSpiReg, mcu ); 
    }
    if( e->hasAttribute("statusreg") )
    {
        QString statReg = e->attribute("statusreg"); 
        m_spi->m_statReg = mcu->getReg( statReg ); 
        watchRegNames( statReg, R_WRITE, m_spi, &McuSpi::writeStatus, mcu ); 
    }
    setInterrupt(  e->attribute("interrupt") , m_spi ); 
    setPrescalers( e->attribute("prescalers"), m_spi ); 

    QStringList pins = e->attribute("pins").remove(" ").split(","); 
    m_spi->setMosiPin( mcu->getIoPin( pins.value(0) ) ); 
    m_spi->setMisoPin( mcu->getIoPin( pins.value(1) ) ); 
    m_spi->setSckPin(  mcu->getIoPin( pins.value(2) ) ); 
    m_spi->setSsPin(   mcu->getIoPin( pins.value(3) ) ); 
}

void McuCreator::createTcp( QDomElement* e )
{
    QString name = e->attribute( "name" ); 

    if( m_core == "scripted"){
        ScriptTcp* su = new ScriptTcp( mcu, name ); 
        m_scriptPerif.push_back( su ); 
        mcu->m_transModules.emplace_back( su ); 
        /// mcu->m_modules.emplace_back( su ); 
    }
    else return; 
}

void McuCreator::createUsi( QDomElement* e )
{
    QString name = e->attribute( "name" ); 
    AvrUsi* usi = nullptr; 
    if( m_core == "AVR" ) usi = new AvrUsi( mcu, name ); 
    else return; 

    mcu->m_modules.emplace_back( usi ); 

    setConfigRegs( e, usi ); 

    /*    if(  e->hasAttribute("dataregs") )
    {
        QString dataReg = e->attribute("dataregs"); 
        usi->m_dataReg = mcu->getReg( dataReg ); 
        watchRegNames( dataReg, R_WRITE, usi, &AvrUsi::writeSpiReg, mcu ); 
    }
    if(  e->hasAttribute("statusreg") )
    {
        QString statReg = e->attribute("statusreg"); 
        usi->m_statusReg = mcu->getReg( statReg ); 
        watchRegNames( statReg, R_WRITE, usi, &AvrUsi::writeStatus, mcu ); 
    }*/

    setInterrupt( e->attribute("interrupt") , usi ); 
    usi->m_startInte = mcu->m_interrupts.m_intList.value( "USISTA" ); 

    usi->setPins( e->attribute("pins").remove(" ") ); 
}

void McuCreator::createWdt( QDomElement* e )
{
    QString name = e->attribute( "name" ); 
    int type = e->attribute("type").toInt(); 
    McuWdt* wdt = nullptr; 
    if     ( m_core == "AVR" )   wdt = AvrWdt::createWdt( mcu, name, type ); 
    else if( m_core == "Pic12" ) wdt = PicWdt::createWdt( mcu, name, type ); 
    else if( m_core == "Pic14" ) wdt = PicWdt::createWdt( mcu, name, type ); 
    else if( m_core == "Pic14e") wdt = PicWdt::createWdt( mcu, name, type ); 

    if( !wdt ) return; 

    mcu->m_modules.emplace_back( wdt ); 
    mcu->m_wdt = wdt; 

    setConfigRegs( e, wdt ); 
    setInterrupt( e->attribute("interrupt"), wdt ); 
    setPrescalers( e->attribute("prescalers"), wdt ); 
}

void McuCreator::createSleep( QDomElement* e )
{
    QString name = e->attribute( "name" ); 
    McuSleep* sleep; 
    if     ( m_core == "AVR"  ) sleep = new AvrSleep( mcu, name ); 
    else if( m_core == "Pic14") sleep = new PicSleep( mcu, name ); 
    else return; 

    mcu->m_modules.emplace_back( sleep ); 
    mcu->m_sleepModule = sleep; 

    setConfigRegs( e, sleep ); 
}

void McuCreator::createDisplay( QDomElement* e )
{
    QString name = e->attribute("name"); 

    int width  = e->attribute("width").toInt(); 
    int height = e->attribute("height").toInt(); 

    DisplayArea* display = nullptr; 
    if( m_core == "scripted" )
    {
        ScriptDisplay* d = new ScriptDisplay( width, height, name, nullptr ); 
        m_scriptPerif.push_back( d ); 
        display = d; 
    }
    else display = new DisplayArea( width, height, name, nullptr ); 

    if( e->hasAttribute("scale") ){
        double scale = e->attribute("scale").toDouble(); 
        display->setMonitorScale( scale ); 
    }
    if( e->hasAttribute("monitorscale") ){
        double scale = e->attribute("monitorscale").toDouble(); 
        display->setMonitorScale( scale ); 
    }
    if( e->hasAttribute("margins") ) m_mcuComp->setMargins( e->attribute("margins") ); 

    bool embed = e->attribute("embeed") == "true"; 
    display->setEmbed( embed ); 
    if( embed ) m_mcuComp->setBackData( display->getBackData() );  // Display in Package
    else        m_displays.append( display );                      // Display in Mcu Monitor
}

void McuCreator::createStack( QDomElement* s )
{
    QStringList spRegs = s->attribute("spreg").split(","); 
    mcu->m_cpu->m_spl = mcu->getReg( spRegs.value(0) ); 
    if( spRegs.size() > 1 )
        mcu->m_cpu->m_sph = mcu->getReg( spRegs.value(1) ); 

    QString inc = s->attribute("increment"); 

    mcu->m_cpu->m_spPre = inc.contains("pre"); 
    mcu->m_cpu->m_spInc = inc.contains("inc") ? 1:-1; 
}

void McuCreator::createInterrupts( QDomElement* i )
{
    QString enable = i->attribute("enable"); 
    if( !enable.isEmpty() )
    {
        mcu->m_interrupts.m_enGlobalFlag = getRegBits( enable, mcu ); 
        watchBitNames( enable, R_WRITE, mcu, &eMcu::enableInterrupts, mcu ); 
    }
    QDomNode node = i->firstChild(); 
    while( !node.isNull() )
    {
        QDomElement el = node.toElement(); 
        if( el.tagName() == "interrupt" ) createInterrupt( &el ); 
        node = node.nextSibling(); 
}   }

void McuCreator::createInterrupt( QDomElement* el )
{
    QString  intName = el->attribute("name"); 
    if( intName.isEmpty() ) return; 

    uint16_t intVector  = el->attribute("vector").toUInt(0,0); 

    Interrupt* iv = nullptr; 
    //if     ( m_core == "8051" )  iv = I51Interrupt::getInterrupt( intName, intVector, mcu ); 
    //else if( m_core == "AVR" )   iv = AVRInterrupt::getInterrupt( intName, intVector, mcu ); 
    if     ( m_core == "Pic14" ) iv = new PicInterrupt( intName, intVector, mcu ); 
    else if( m_core == "Pic14e") iv = new Pic14eInterrupt( intName, intVector, mcu ); 
    else                         iv = new Interrupt( intName, intVector, mcu ); 
    if( !iv ) return; 

    mcu->m_interrupts.m_intList.insert( intName, iv ); 
    iv->m_interrupts = &(mcu->m_interrupts); 

    QString enable = el->attribute("enable"); 
    if( !enable.isEmpty() ) watchBitNames( enable, R_WRITE, iv, &Interrupt::enableFlag, mcu ); 

    QString intFlag = el->attribute("flag"); 
    iv->m_flagMask = mcu->m_bitMasks.value( intFlag ); 
    iv->m_flagReg  = mcu->m_bitRegs.value( intFlag ); 

    QString intPrio = el->attribute("priority"); 
    bool ok = false; 
    uint8_t prio = intPrio.toUInt(&ok,0); 

    if( ok ) iv->setPriority( prio ); 
    else     watchBitNames( intPrio, R_WRITE, iv, &Interrupt::setPriority, mcu ); 

    if( m_core != "AVR" ) /// TODO find a generic way
    {
        watchBitNames( intFlag, R_WRITE, iv, &Interrupt::flagCleared, mcu ); 
    }
    if( el->hasAttribute("clear") ) // If clear="1" Clear flag by writting 1 to it
    {
        uint8_t val = el->attribute("clear").toUInt(); 
        if( val ) watchRegister( iv->m_flagReg, R_WRITE, iv, &Interrupt::writeFlag, mcu ); 
    }
    if( el->hasAttribute("autoclear") ) // If autoclear="0" deactivate autoclear
    {
        uint8_t val = el->attribute("autoclear").toUInt(); 
        if( val ) iv->m_autoClear = true; 
        else      iv->m_autoClear = false; 
    }
    if( el->hasAttribute("pin") )
    {
        iv->m_intPin = mcu->getIoPin( el->attribute("pin") ); 
    }
    if( el->hasAttribute("wakeup") )
    {
        iv->m_wakeup = el->attribute("wakeup").toUInt( 0, 2 ); 
}   }

void McuCreator::setInterrupt( QString intName, McuModule* module )
{
    if( intName.isEmpty() ) return; 
    Interrupt* inte = mcu->m_interrupts.m_intList.value( intName ); 
    if( !inte ) qDebug() << "    Error: Interrupt not found: " << intName << Qt::endl; 
    module->setInterrupt( inte ); 
}

void McuCreator::setConfigRegs( QDomElement* u, McuModule* module )
{
    if( u->hasAttribute("configregsA") )
    {
        QString regs = u->attribute("configregsA"); 
        watchRegNames( regs, R_WRITE, module, &McuModule::configureA, mcu ); 
    }
    if( u->hasAttribute("configregsB") )
    {
        QString regs = u->attribute("configregsB"); 
        watchRegNames( regs, R_WRITE, module, &McuModule::configureB, mcu ); 
    }
    if( u->hasAttribute("configregsC") )
    {
        QString regs = u->attribute("configregsC"); 
        watchRegNames( regs, R_WRITE, module, &McuModule::configureC, mcu ); 
    }
    if( u->hasAttribute("configbitsA") )
    {
        QString configBits = u->attribute("configbitsA"); 
        watchBitNames( configBits, R_WRITE, module, &McuModule::configureA, mcu ); 
        module->m_configBitsA = getRegBits( configBits, mcu ); 
    }
    if( u->hasAttribute("configbitsB") )
    {
        QString configBits = u->attribute("configbitsB"); 
        watchBitNames( configBits, R_WRITE, module, &McuModule::configureB, mcu ); 
        module->m_configBitsB = getRegBits( configBits, mcu ); 
    }
    if( u->hasAttribute("configbitsC") )
    {
        QString configBits = u->attribute("configbitsC"); 
        watchBitNames( configBits, R_WRITE, module, &McuModule::configureC, mcu ); 
        module->m_configBitsC = getRegBits( configBits, mcu ); 
}   }

void McuCreator::setPrescalers( QString pr, McuPrescaled* module )
{
    if( pr.isEmpty() ) return; 
    QStringList prescalers = pr.remove(" ").split(","); 
    module->m_prescList.resize( prescalers.size() ); 

    for( int i=0;  i<prescalers.size();  ++i )
    {
        QString prStr = prescalers.value(i); 
        uint prVal; 
        if     ( prStr == "EXT_F" ) prVal = 0x8000;  // Ext clock, falling edge
        else if( prStr == "EXT_R" ) prVal = 0x8001;  // Ext clock, rising edge
        else                        prVal = prStr.toUInt(); 
        module->m_prescList[i] = prVal; 
    }
}
