summaryrefslogtreecommitdiffstats
path: root/microbe
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-24 01:49:02 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-24 01:49:02 +0000
commit5de3dd4762ca33a0f92e79ffa4fe2ff67069d531 (patch)
treebad482b7afa4cdf47422d60a5dd2c61c7e333b09 /microbe
downloadktechlab-5de3dd4762ca33a0f92e79ffa4fe2ff67069d531.tar.gz
ktechlab-5de3dd4762ca33a0f92e79ffa4fe2ff67069d531.zip
Added KDE3 version of ktechlab
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/ktechlab@1095338 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'microbe')
-rw-r--r--microbe/Makefile.am10
-rw-r--r--microbe/btreebase.cpp248
-rw-r--r--microbe/btreebase.h59
-rw-r--r--microbe/btreenode.cpp66
-rw-r--r--microbe/btreenode.h107
-rw-r--r--microbe/expression.cpp842
-rw-r--r--microbe/expression.h126
-rw-r--r--microbe/instruction.cpp2309
-rw-r--r--microbe/instruction.h1273
-rw-r--r--microbe/main.cpp80
-rw-r--r--microbe/microbe.cpp472
-rw-r--r--microbe/microbe.h249
-rw-r--r--microbe/optimizer.cpp512
-rw-r--r--microbe/optimizer.h87
-rw-r--r--microbe/parser.cpp1054
-rw-r--r--microbe/parser.h293
-rw-r--r--microbe/pic14.cpp1196
-rw-r--r--microbe/pic14.h253
-rw-r--r--microbe/traverser.cpp100
-rw-r--r--microbe/traverser.h72
-rw-r--r--microbe/variable.cpp79
-rw-r--r--microbe/variable.h79
22 files changed, 9566 insertions, 0 deletions
diff --git a/microbe/Makefile.am b/microbe/Makefile.am
new file mode 100644
index 0000000..da467f9
--- /dev/null
+++ b/microbe/Makefile.am
@@ -0,0 +1,10 @@
+INCLUDES = $(all_includes)
+METASOURCES = AUTO
+bin_PROGRAMS = microbe
+microbe_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+microbe_SOURCES = btreebase.cpp btreenode.cpp main.cpp traverser.cpp \
+ expression.cpp pic14.cpp variable.cpp optimizer.cpp instruction.cpp microbe.cpp \
+ parser.cpp
+noinst_HEADERS = btreebase.h btreenode.h traverser.h pic14.h variable.h \
+ optimizer.h microbe.h parser.h
+microbe_LDADD = -lpthread $(LIB_KFILE)
diff --git a/microbe/btreebase.cpp b/microbe/btreebase.cpp
new file mode 100644
index 0000000..bd9e38a
--- /dev/null
+++ b/microbe/btreebase.cpp
@@ -0,0 +1,248 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "btreebase.h"
+#include "traverser.h"
+#include "parser.h"
+#include "pic14.h"
+
+BTreeBase::BTreeBase()
+{
+ m_root = 0L;
+}
+
+void BTreeBase::deleteTree()
+{
+ if(m_root) m_root->deleteChildren();
+ delete m_root;
+ m_root = 0L;
+}
+
+BTreeBase::~BTreeBase()
+{
+ deleteTree();
+}
+
+
+void BTreeBase::addNode(BTreeNode *parent, BTreeNode *node, bool left)
+{
+ // Debugging lines, remove when expression parsing has been completed.
+ //if(!parent) cerr<<"Null parent pointer!\n";
+ //if(!node) cerr<<"Null node pointer!\n");
+
+ if(left) parent->setLeft(node);
+ else parent->setRight(node);
+}
+
+void BTreeBase::pruneTree(BTreeNode *root, bool /*conditionalRoot*/)
+{
+ Traverser t(root);
+
+ t.descendLeftwardToTerminal();
+ bool done = false;
+ while(!done)
+ {
+ //t.descendLeftwardToTerminal();
+ if( t.current()->parent() )
+ {
+ if( t.oppositeNode()->hasChildren() ) pruneTree(t.oppositeNode());
+ }
+
+ t.moveToParent();
+ if( !t.current()->hasChildren() )
+ {
+ //if(t.current() == t.root()) done = true;
+ if(!t.current()->parent()) done = true;
+ continue;
+ }
+
+ BTreeNode *l = t.current()->left();
+ BTreeNode *r = t.current()->right();
+ BTreeNode *n = 0;
+ BTreeNode *z = 0;
+
+
+ // Deal with situations where there are two constants so we want
+ // to evaluate at compile time
+ if( (l->type() == number && r->type() == number) ) // && !(t.current()==root&&conditionalRoot) )
+ {
+ if(t.current()->childOp() == Expression::division && r->value() == "0" )
+ {
+ t.current()->setChildOp(Expression::divbyzero);
+ return;
+ }
+ QString value = QString::number(Parser::doArithmetic(l->value().toInt(),r->value().toInt(),t.current()->childOp()));
+ t.current()->deleteChildren();
+ t.current()->setChildOp(Expression::noop);
+ t.current()->setType(number);
+ t.current()->setValue(value);
+ }
+
+ // Addition and subtraction
+ else if(t.current()->childOp() == Expression::addition || t.current()->childOp() == Expression::subtraction)
+ {
+ // See if one of the nodes is 0, and set n to the node that actually has data,
+ // z to the one containing zero.
+ bool zero = false;
+ if( l->value() == "0" )
+ {
+ zero = true;
+ n = r;
+ z = l;
+ }
+ else if( r->value() == "0" )
+ {
+ zero = true;
+ n = l;
+ z = r;
+ }
+ // Now get rid of the useless nodes
+ if(zero)
+ {
+ BTreeNode *p = t.current(); // save in order to delete after
+
+ replaceNode(p,n);
+ t.setCurrent(n);
+ // Delete the old nodes
+ delete p;
+ delete z;
+ }
+ }
+
+ // Multiplication and division
+ else if(t.current()->childOp() == Expression::multiplication || t.current()->childOp() == Expression::division)
+ {
+ // See if one of the nodes is 0, and set n to the node that actually has data,
+ // z to the one containing zero.
+ bool zero = false;
+ bool one = false;
+ if( l->value() == "1" )
+ {
+ one = true;
+ n = r;
+ z = l;
+ }
+ else if( r->value() == "1" )
+ {
+ one = true;
+ n = l;
+ z = r;
+ }
+ if( l->value() == "0" )
+ {
+ zero = true;
+ n = r;
+ z = l;
+ }
+ else if( r->value() == "0" )
+ {
+
+ // since we can't call compileError from in this class, we have a special way of handling it:
+ // Leave the children as they are, and set childOp to divbyzero
+ if( t.current()->childOp() == Expression::division )
+ {
+ t.current()->setChildOp(Expression::divbyzero);
+ return; // no point doing any more since we are going to raise a compileError later anyway.
+ }
+ zero = true;
+ n = l;
+ z = r;
+ }
+ // Now get rid of the useless nodes
+ if(one)
+ {
+ BTreeNode *p = t.current(); // save in order to delete after
+ replaceNode(p,n);
+ t.setCurrent(n);
+ // Delete the old nodes
+ delete p;
+ delete z;
+ }
+ if(zero)
+ {
+ BTreeNode *p = t.current();
+ p->deleteChildren();
+ p->setChildOp(Expression::noop);
+ p->setType(number);
+ p->setValue("0");
+
+ }
+ }
+ else if( t.current()->childOp() == Expression::bwand || t.current()->childOp() == Expression::bwor || t.current()->childOp() == Expression::bwxor )
+ {
+ bool zero = false;
+ if( l->value() == "0" )
+ {
+ zero = true;
+ n = r;
+ z = l;
+ }
+ else if( r->value() == "0" )
+ {
+ zero = true;
+ n = l;
+ z = r;
+ }
+ // Now get rid of the useless nodes
+ if(zero)
+ {
+ BTreeNode *p = t.current();
+ QString value;
+ if( p->childOp() == Expression::bwand )
+ {
+ value = "0";
+ p->deleteChildren();
+ p->setChildOp(Expression::noop);
+ p->setType(number);
+ }
+ if( p->childOp() == Expression::bwor || p->childOp() == Expression::bwxor )
+ {
+ value = n->value();
+ BTreeNode *p = t.current(); // save in order to delete after
+ replaceNode(p,n);
+ t.setCurrent(n);
+ // Delete the old nodes
+ delete p;
+ delete z;
+ }
+ p->setValue(value);
+ }
+ }
+
+ if(!t.current()->parent() || t.current() == root) done = true;
+ else
+ {
+
+ }
+ }
+}
+
+void BTreeBase::replaceNode(BTreeNode *node, BTreeNode *replacement)
+{
+ // (This works under the assumption that a node is not linked to two places at once).
+ if( !node->parent() )
+ {
+ setRoot(replacement);
+ replacement->setParent(0L);
+ return;
+ }
+ if( node->parent()->left() == node ) node->parent()->setLeft(replacement);
+ if( node->parent()->right() == node ) node->parent()->setRight(replacement);
+}
diff --git a/microbe/btreebase.h b/microbe/btreebase.h
new file mode 100644
index 0000000..d8d1040
--- /dev/null
+++ b/microbe/btreebase.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTREEBASE_H
+#define BTREEBASE_H
+#include "microbe.h"
+#include "btreenode.h"
+
+/**
+@short This holds a pointer to the start of the tree, and provides the traversal code.
+@author Daniel Clarke
+*/
+class BTreeBase{
+public:
+ BTreeBase();
+ ~BTreeBase();
+
+ /** Return a pointer to the root node of the tree */
+ BTreeNode *root() const { return m_root; }
+
+ /** Set the root node of the tree */
+ void setRoot(BTreeNode *root){m_root = root; }
+
+ /** Link the node into the tree. a.t.m all this really
+ does it sets the parent/child relationship pointers,
+ but is used in case something needs to be changed in the future
+ Added to the left if left == true or the right if left == false */
+ void addNode(BTreeNode *parent, BTreeNode *node, bool left);
+
+ /** Deletes all nodes in tree and zeros pointer to root node */
+ void deleteTree();
+
+ /** Tidies the tree up; merging constants and removing redundant branches */
+ void pruneTree(BTreeNode *root, bool conditionalRoot = true);
+
+ /** Put a node in place of another, linking it correctly into the parent. */
+ void replaceNode(BTreeNode *node, BTreeNode *replacement);
+
+protected:
+ BTreeNode *m_root;
+};
+
+#endif
diff --git a/microbe/btreenode.cpp b/microbe/btreenode.cpp
new file mode 100644
index 0000000..27d49cc
--- /dev/null
+++ b/microbe/btreenode.cpp
@@ -0,0 +1,66 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "btreenode.h"
+#include "pic14.h"
+
+BTreeNode::BTreeNode()
+{
+ m_parent = 0L;
+ m_left = 0L;
+ m_right = 0L;
+ m_type = unset;
+}
+
+BTreeNode::BTreeNode(BTreeNode *p, BTreeNode *l, BTreeNode *r)
+{
+ m_parent = p;
+ m_left = l;
+ m_right = r;
+}
+
+BTreeNode::~BTreeNode()
+{
+ // Must not delete children as might be unlinking!!! deleteChildren();
+}
+
+void BTreeNode::deleteChildren()
+{
+ if(m_left)
+ {
+ m_left->deleteChildren();
+ delete m_left;
+ }
+ if(m_right)
+ {
+ m_right->deleteChildren();
+ delete m_right;
+ }
+
+ m_left = 0L;
+ m_right = 0L;
+
+ return;
+}
+
+// void BTreeNode::printTree()
+// {
+//
+// }
diff --git a/microbe/btreenode.h b/microbe/btreenode.h
new file mode 100644
index 0000000..7f5fdfb
--- /dev/null
+++ b/microbe/btreenode.h
@@ -0,0 +1,107 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef BTREENODE_H
+#define BTREENODE_H
+
+#include "btreebase.h"
+#include "expression.h"
+
+#include <qstring.h>
+#include <qptrlist.h>
+
+/**
+A node points to the two child nodes (left and right), and contains the binary
+operation used to combine them.
+
+@author Daniel Clarke
+@author David Saxton
+*/
+class BTreeNode
+{
+ public:
+ BTreeNode();
+ BTreeNode(BTreeNode *p, BTreeNode *l, BTreeNode *r);
+ ~BTreeNode();
+
+ /**
+ * Used for debugging purposes; prints the tree structure to stdout.
+ */
+// void printTree();
+ /**
+ * Recursively delete all children of a node.
+ */
+ void deleteChildren();
+ /**
+ * @return the parent node.
+ */
+ BTreeNode *parent() const { return m_parent; }
+ /**
+ * @return the left child node.
+ */
+ BTreeNode *left() const { return m_left; }
+ /**
+ * @return the right child node.
+ */
+ BTreeNode *right() const { return m_right; }
+ void setParent(BTreeNode *parent) { m_parent = parent; }
+ /**
+ * Set the child node on the left to the one give, and reparents it to
+ * this node.
+ */
+ void setLeft(BTreeNode *left) { m_left = left; m_left->setParent( this ); }
+ /**
+ * Set the child node on the right to the one give, and reparents it to
+ * this node.
+ */
+ void setRight(BTreeNode *right) { m_right = right; m_right->setParent( this ); }
+ /**
+ * @return true if have a left or a right child node.
+ */
+ bool hasChildren() const { return m_left || m_right; }
+
+ ExprType type() const {return m_type;}
+ void setType(ExprType type) { m_type = type; }
+ QString value() const {return m_value;}
+ void setValue( const QString & value ) { m_value = value; }
+
+ Expression::Operation childOp() const {return m_childOp;}
+ void setChildOp(Expression::Operation op){ m_childOp = op;}
+
+ void setReg( const QString & r ){ m_reg = r; }
+ QString reg() const {return m_reg;}
+
+ bool needsEvaluating() const { return hasChildren(); }
+
+ protected:
+ BTreeNode *m_parent;
+ BTreeNode *m_left;
+ BTreeNode *m_right;
+
+ /** This is used to remember what working register contains the value of the node during assembly.*/
+ QString m_reg;
+
+ ExprType m_type;
+ QString m_value;
+
+ Expression::Operation m_childOp;
+};
+
+#endif
diff --git a/microbe/expression.cpp b/microbe/expression.cpp
new file mode 100644
index 0000000..33fd514
--- /dev/null
+++ b/microbe/expression.cpp
@@ -0,0 +1,842 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "btreebase.h"
+#include "btreenode.h"
+#include "expression.h"
+#include "traverser.h"
+#include "parser.h"
+#include "pic14.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <qregexp.h>
+
+Expression::Expression( PIC14 *pic, Microbe *master, SourceLine sourceLine, bool suppressNumberTooBig )
+ : m_sourceLine(sourceLine)
+{
+ m_pic = pic;
+ mb = master;
+ m_bSupressNumberTooBig = suppressNumberTooBig;
+}
+
+Expression::~Expression()
+{
+}
+
+void Expression::traverseTree( BTreeNode *root, bool conditionalRoot )
+{
+ Traverser t(root);
+ t.start();
+
+ // special case: if we are starting at the root node then
+ // we are dealing with something of the form variable = 6
+ // or variable = portb
+ ///TODO reimplement assignments as two branched trees?
+ if ( t.current() == root &&
+ !root->hasChildren() &&
+ t.current()->childOp() != pin &&
+ t.current()->childOp() != notpin &&
+ t.current()->childOp() != function &&
+ t.current()->childOp() != read_keypad )
+ {
+ switch(root->type())
+ {
+ case number: m_pic->assignNum(root->value()); break;
+ case variable: m_pic->assignVar(root->value()); break;
+ default: break; // Should never get here
+ }
+ // no need to traverse the tree as there is none.
+ return;
+ }
+
+ t.setCurrent(root);
+
+ if(t.current()->hasChildren())
+ {
+ // Here we work out what needs evaulating, and in which order.
+ // To minimize register usage, if only one branch needs traversing,
+ // then that branch should be done first.
+ bool evaluateLeft = t.current()->left()->needsEvaluating();
+
+ BTreeNode *evaluateFirst;
+ BTreeNode *evaluateSecond;
+
+ // If both need doing, then it really doesn't matter which we do
+ // first (unless we are looking to do really complex optimizations...
+
+ // Cases:
+ // - Both need evaluating,
+ // - or left needs doing first,
+ // in both cases we evaluate left, then right.
+ if( evaluateLeft )
+ {
+ evaluateFirst = t.current()->left();
+ evaluateSecond = t.current()->right();
+ }
+ // Otherwise it is best to evaluate right first for reasons given above.
+ else
+ {
+ evaluateFirst = t.current()->right();
+ evaluateSecond = t.current()->left();
+ }
+
+ QString dest1 = mb->dest();
+ mb->incDest();
+ QString dest2 = mb->dest();
+ mb->decDest();
+
+ bool evaluated = false;
+ if( evaluateFirst->hasChildren() )
+ {
+ traverseTree(evaluateFirst);
+ evaluated = true;
+ }
+ else if( isUnaryOp(evaluateFirst->childOp()) )
+ {
+ doUnaryOp( evaluateFirst->childOp(), evaluateFirst );
+ evaluated = true;
+ }
+ if ( evaluated )
+ {
+ // We need to save the result if we are going tro traverse the other
+ // branch, or if we are performing a subtraction in which case the
+ // value wanted in working is not the current value.
+ // But as the optimizer will deal with unnecessary variables anyway,
+ // always save to a register
+
+ evaluateFirst->setReg( dest1 );
+ evaluateFirst->setType( variable );
+ m_pic->saveToReg( dest1 );
+ }
+
+ evaluated = false;
+ if( evaluateSecond->hasChildren() )
+ {
+ mb->incDest();
+ mb->incDest();
+ traverseTree(evaluateSecond);
+ evaluated = true;
+ mb->decDest();
+ mb->decDest();
+ }
+ else if( isUnaryOp(evaluateSecond->childOp()) )
+ {
+ doUnaryOp( evaluateSecond->childOp(), evaluateSecond );
+ evaluated = true;
+ }
+ if ( evaluated )
+ {
+ evaluateSecond->setReg( dest2 );
+ evaluateSecond->setType( variable );
+ m_pic->saveToReg( dest2 );
+ }
+ }
+
+ if(t.current()->childOp()==divbyzero)
+ {
+ mistake( Microbe::DivisionByZero );
+ }
+
+ // If we are at the top level of something like 'if a == 3 then', then we are ready to put
+ // in the if code, else the expression just evaluates to 0 or 1
+ if(conditionalRoot && t.current() == root)
+ m_pic->setConditionalCode(m_ifCode, m_elseCode);
+
+ // Handle operations
+ // (functions are not actually supported)
+ if(isUnaryOp(t.current()->childOp()))
+ doUnaryOp( t.current()->childOp(), t.current() );
+ else
+ doOp( t.current()->childOp(), t.current()->left(), t.current()->right() );
+
+}
+
+void Expression::doOp( Operation op, BTreeNode *left, BTreeNode *right )
+{
+ QString lvalue;
+ if(left->reg().isEmpty())
+ lvalue = left->value();
+ else
+ lvalue = left->reg();
+
+ QString rvalue;
+ if(right->reg().isEmpty())
+ rvalue = right->value();
+ else
+ rvalue = right->reg();
+
+ // Handle if stuff
+ PIC14::LocationType leftType;
+ switch ( left->type() )
+ {
+ case number:
+ leftType = PIC14::num;
+ break;
+
+ case variable:
+ leftType = PIC14::var;
+ break;
+
+ case working:
+ leftType = PIC14::work;
+ break;
+
+ case unset:
+ case extpin:
+ case keypad:
+ kdError() << k_funcinfo << "Bad left->type(): " << left->type() << endl;
+ };
+
+ PIC14::LocationType rightType;
+ switch ( right->type() )
+ {
+ case number:
+ rightType = PIC14::num;
+ break;
+
+ case variable:
+ rightType = PIC14::var;
+ break;
+
+ case working:
+ rightType = PIC14::work;
+ break;
+
+ case unset:
+ case extpin:
+ case keypad:
+ kdError() << k_funcinfo << "Bad right->type(): " << right->type() << endl;
+ };
+
+ switch(op)
+ {
+ case equals: m_pic->equal( lvalue, rvalue, leftType, rightType ); break;
+ case notequals: m_pic->notEqual( lvalue, rvalue, leftType, rightType ); break;
+ case lt: m_pic->lessThan( lvalue, rvalue, leftType, rightType ); break;
+ case gt: m_pic->greaterThan( lvalue, rvalue, leftType, rightType ); break;
+ case le: m_pic->lessOrEqual( lvalue, rvalue, leftType, rightType ); break;
+ case ge: m_pic->greaterOrEqual( lvalue, rvalue, leftType, rightType ); break;
+
+ case addition: m_pic->add( lvalue, rvalue, leftType, rightType ); break;
+ case subtraction: m_pic->subtract( lvalue, rvalue, leftType, rightType ); break;
+ case multiplication: m_pic->mul( lvalue, rvalue, leftType, rightType ); break;
+ case division: m_pic->div( lvalue, rvalue, leftType, rightType ); break;
+
+ case bwand: m_pic->bitwise( bwand, lvalue, rvalue, leftType, rightType ); break;
+ case bwor: m_pic->bitwise( bwor, lvalue, rvalue, leftType, rightType ); break;
+ case bwxor: m_pic->bitwise( bwxor, lvalue, rvalue, leftType, rightType ); break;
+ case bwnot: m_pic->bitwise( bwnot, lvalue, rvalue, leftType, rightType ); break;
+
+ default: break;
+ }
+}
+
+void Expression::buildTree( const QString & unstrippedExpression, BTreeBase *tree, BTreeNode *node, int level )
+{
+ int firstEnd = 0;
+ int secondStart = 0;
+ bool unary = false;
+ Operation op;
+ QString expression = stripBrackets( unstrippedExpression );
+ switch(level)
+ {
+ // ==, !=
+ case 0:
+ {
+ int equpos = findSkipBrackets(expression, "==");
+ int neqpos = findSkipBrackets(expression, "!=");
+ if( equpos != -1 )
+ {
+ op = equals;
+ firstEnd = equpos;
+ secondStart = equpos + 2;
+ }
+ else if( neqpos != -1 )
+ {
+ op = notequals;
+ firstEnd = neqpos;
+ secondStart = neqpos + 2;
+ }
+ else op = noop;
+ break;
+ }
+
+ // <, <=, >=, >
+ case 1:
+ {
+ int ltpos = findSkipBrackets(expression, "<");
+ int lepos = findSkipBrackets(expression, "<=");
+ int gepos = findSkipBrackets(expression, ">=");
+ int gtpos = findSkipBrackets(expression, ">");
+ // Note: if (for example) "<=" is present, "<" will also be present. This
+ // means that we have to check for "<=" before "<", etc.
+ if( lepos != -1 )
+ {
+ op = le;
+ firstEnd = lepos;
+ secondStart = lepos + 2;
+ }
+ else if( gepos != -1 )
+ {
+ op = ge;
+ firstEnd = gepos;
+ secondStart = gepos + 2;
+ }
+ else if( ltpos != -1 )
+ {
+ op = lt;
+ firstEnd = ltpos;
+ secondStart = ltpos + 1;
+ }
+ else if( gtpos != -1 )
+ {
+ op = gt;
+ firstEnd = gtpos;
+ secondStart = gtpos + 1;
+ }
+ else op = noop;
+ break;
+ }
+
+ // +,-
+ case 2:
+ {
+ int addpos = findSkipBrackets(expression, '+');
+ int subpos = findSkipBrackets(expression, '-');
+ if( subpos != -1 )
+ {
+ op = subtraction;
+ firstEnd = subpos;
+ secondStart = subpos + 1;
+ }
+ else if( addpos != -1 )
+ {
+ op = addition;
+ firstEnd = addpos;
+ secondStart = addpos + 1;
+ }
+ else op = noop;
+ break;
+ }
+
+ // *,/
+ case 3:
+ {
+ int mulpos = findSkipBrackets(expression, '*');
+ int divpos = findSkipBrackets(expression, '/');
+ if( divpos != -1 )
+ {
+ op = division;
+ firstEnd = divpos;
+ secondStart = divpos + 1;
+ }
+ else if( mulpos != -1 )
+ {
+ op = multiplication;
+ firstEnd = mulpos;
+ secondStart = mulpos + 1;
+ }
+ else op = noop;
+ break;
+ }
+
+ // ^
+ case 4:
+ {
+ int exppos = findSkipBrackets(expression, '^');
+ if( exppos != -1 )
+ {
+ op = exponent;
+ firstEnd = exppos;
+ secondStart = exppos + 1;
+ }
+ else op = noop;
+ break;
+ }
+
+ // AND, OR, XOR
+ case 5:
+ {
+ int bwAndPos = findSkipBrackets(expression, " AND ");
+ int bwOrPos = findSkipBrackets(expression, " OR ");
+ int bwXorPos = findSkipBrackets(expression, " XOR ");
+ if( bwAndPos != -1 )
+ {
+ op = bwand;
+ firstEnd = bwAndPos;
+ secondStart = bwAndPos + 5;
+ }
+ else if( bwOrPos != -1 )
+ {
+ op = bwor;
+ firstEnd = bwOrPos;
+ secondStart = bwOrPos + 4;
+ }
+ else if( bwXorPos != -1 )
+ {
+ op = bwxor;
+ firstEnd = bwXorPos;
+ secondStart = bwXorPos + 5;
+ }
+ else op = noop;
+ break;
+ }
+
+ // NOT
+ case 6:
+ {
+ int bwNotPos = findSkipBrackets(expression, " NOT ");
+ if( bwNotPos != -1 )
+ {
+ op = bwnot;
+ unary = true;
+ firstEnd = bwNotPos; // this line is not needed for unary things/
+ secondStart = bwNotPos + 5;
+ }
+ else op = noop;
+ break;
+ }
+ }
+
+ node->setChildOp(op);
+
+ QString tokens[2];
+ tokens[0] = expression.left(firstEnd).stripWhiteSpace();
+ tokens[1] = expression.mid(secondStart).stripWhiteSpace();
+
+ if( op != noop )
+ {
+ for( int j = 0; j < 2; j++ )
+ {
+
+ BTreeNode *newNode = new BTreeNode();
+ tree->addNode( node, newNode, (j == 0) );
+ // we need to strip any brackets from the sub-expression
+
+ // try each token again at the same level, if they
+ // don't have any of this level's operators, then the function
+ // will go to the next level as below.
+
+ // For unary opertaions, e.g NOT, we have no special
+ // code for nodes with only one child, so we leave the left
+ // hand child blank and put the rest in the right hand node.
+ if( unary && j == 0 )
+ {
+ newNode->setValue("");
+ newNode->setType(number);
+ }
+ else buildTree(tokens[j], tree, newNode, 0 );
+ }
+ }
+ else
+ {
+ // if there was no relevant operation i.e. " 3*4 / 6" as opposed to " 3*4 + 6"
+ // then just pass the node onto the next parsing level.
+ // unless we are at the lowest level, in which case we have reached a final value.
+ if( level == 6 ) expressionValue(expression,tree,node);
+ else
+ {
+ buildTree(expression,tree,node,level + 1);
+ }
+ }
+}
+
+void Expression::doUnaryOp(Operation op, BTreeNode *node)
+{
+ /* Note that this isn't for unary operations as such,
+ rather for things that are operations that have no direct children,
+ e.g. portx.n is high, and functionname(args)*/
+
+ if ( op == pin || op == notpin )
+ m_pic->Spin( m_pic->toPortPin( node->value() ), (op==notpin) );
+
+ else if ( op == read_keypad )
+ m_pic->Skeypad( mb->variable( node->value() ) );
+}
+
+void Expression::compileExpression( const QString & expression )
+{
+ // Make a tree to put the expression in.
+ BTreeBase *tree = new BTreeBase();
+ BTreeNode *root = new BTreeNode();
+
+ // parse the expression into the tree
+ buildTree(expression,tree,root,0);
+ // compile the tree into assembly code
+ tree->setRoot(root);
+ tree->pruneTree(tree->root());
+ traverseTree(tree->root());
+
+ // Note deleting the tree deletes all nodes, so the root
+ // doesn't need deleting separately.
+ delete tree;
+ return;
+}
+
+void Expression::compileConditional( const QString & expression, Code * ifCode, Code * elseCode )
+{
+ if( expression.contains(QRegExp("=>|=<|=!")) )
+ {
+ mistake( Microbe::InvalidComparison, expression );
+ return;
+ }
+ if( expression.contains(QRegExp("[^=><!][=][^=]")))
+ {
+ mistake( Microbe::InvalidEquals );
+ return;
+ }
+ // Make a tree to put the expression in.
+ BTreeBase *tree = new BTreeBase();
+ BTreeNode *root = new BTreeNode();
+
+ // parse the expression into the tree
+ buildTree(expression,tree,root,0);
+
+ // Modify the tree so it is always at the top level of the form (kwoerpkwoep) == (qwopekqpowekp)
+ if ( root->childOp() != equals &&
+ root->childOp() != notequals &&
+ root->childOp() != gt &&
+ root->childOp() != lt &&
+ root->childOp() != ge &&
+ root->childOp() != le &&
+ root->childOp() != pin &&
+ root->childOp() != notpin &&
+ root->childOp() != read_keypad )
+ {
+ BTreeNode *newRoot = new BTreeNode();
+
+ BTreeNode *oneNode = new BTreeNode();
+ oneNode->setChildOp(noop);
+ oneNode->setType(number);
+ oneNode->setValue("1");
+
+ newRoot->setLeft(root);
+ newRoot->setRight(oneNode);
+ newRoot->setType(unset);
+ newRoot->setChildOp(ge);
+
+ tree->setRoot(newRoot);
+ root = newRoot;
+ }
+ // compile the tree into assembly code
+ tree->setRoot(root);
+ tree->pruneTree(tree->root(),true);
+
+ // We might have just a constant expression, in which case we can just always do if or else depending
+ // on whether it is true or false.
+ if( root->childOp() == noop )
+ {
+ if( root->value().toInt() == 0 )
+ m_pic->mergeCode( elseCode );
+ else
+ m_pic->mergeCode( ifCode );
+ return;
+ }
+
+ // traverse tree with argument conditionalRoot true
+ // so that 3 == x gets integrated with code for if, repeat until etc...
+ m_ifCode = ifCode;
+ m_elseCode = elseCode;
+ traverseTree(tree->root(),true);
+
+ // Note deleting the tree deletes all nodes, so the root
+ // doesn't need deleting separately.
+ delete tree;
+}
+
+bool Expression::isUnaryOp(Operation op)
+{
+ return op == pin || op == notpin || op == function || op == read_keypad;
+}
+
+
+void Expression::mistake( Microbe::MistakeType type, const QString & context )
+{
+ mb->compileError( type, context, m_sourceLine );
+}
+
+int Expression::findSkipBrackets( const QString & expr, char ch, int startPos)
+{
+ bool found = false;
+ int i = startPos;
+ int bracketLevel = 0;
+ while(!found)
+ {
+ if(expr[i].latin1() == '\'')
+ {
+ if( i + 2 < int(expr.length()) )
+ {
+ if( expr[i+2].latin1() == '\'' )
+ {
+ i = i + 2;
+ found = true;
+ }
+ }
+ }
+
+ if(expr[i].latin1() == '(') bracketLevel++;
+ else if(expr[i].latin1() == ')') bracketLevel--;
+
+ if( bracketLevel == 0 )
+ {
+ if(expr[i].latin1() == ch) found = true;
+ else i++;
+ }
+ else i++;
+
+ if( i >= int(expr.length()) )
+ {
+ found = true;
+ i = -1;
+ }
+ }
+ return i;
+}
+
+int Expression::findSkipBrackets( const QString & expr, QString phrase, int startPos)
+{
+ bool found = false;
+ int i = startPos;
+ int bracketLevel = 0;
+ while(!found)
+ {
+ if(expr[i].latin1() == '\'')
+ {
+ if( i + 2 < int(expr.length()) )
+ {
+ if( expr[i+2].latin1() == '\'' )
+ {
+ i = i + 2;
+ found = true;
+ }
+ }
+ }
+
+ if(expr[i].latin1() == '(') bracketLevel++;
+ else if(expr[i].latin1() == ')') bracketLevel--;
+
+ if( bracketLevel == 0 )
+ {
+ if(expr.mid(i,phrase.length()) == phrase) found = true;
+ else i++;
+ }
+ else i++;
+
+ if( i >= int(expr.length()) )
+ {
+ found = true;
+ i = -1;
+ }
+ }
+ return i;
+}
+
+QString Expression::stripBrackets( QString expression )
+{
+ bool stripping = true;
+ int bracketLevel = 0;
+ int i = 0;
+ expression = expression.stripWhiteSpace();
+ while(stripping)
+ {
+ if( expression.at(i) == '(' ) bracketLevel++;
+ else if( expression.at(i) == ')' )
+ {
+ if( i == int(expression.length() - 1) && bracketLevel == 1)
+ {
+ expression = expression.mid(1,expression.length() - 2).stripWhiteSpace();
+ }
+ bracketLevel--;
+ }
+ if( i == int(expression.length() - 1) && bracketLevel > 0 )
+ {
+ mistake( Microbe::MismatchedBrackets, expression );
+ // Stray brackets might cause the expressionession parser some problems,
+ // so we just avoid parsing anything altogether
+ expression = "";
+ stripping = false;
+ }
+ i++;
+ if( bracketLevel == 0 ) stripping = false;
+ }
+ return expression;
+}
+
+void Expression::expressionValue( QString expr, BTreeBase */*tree*/, BTreeNode *node)
+{
+ /* The "end of the line" for the expression parsing, the
+ expression has been broken down into the fundamental elements of expr.value()=="to"||
+ variable, number, special etc... so we now just set value and type */
+
+
+
+ /* Alternatively we might have a function call
+ e.g. somefunction(3,potatoes,hairstyle + 6)
+ In which case we need to call back to parseExpr to process the arguments,
+ saving them on the basic stack then making the function call.
+ Of course we also need to mark the terminal node type as a function.
+ */
+ expr = expr.stripWhiteSpace();
+
+ // My intention is so that these error checks are ordered
+ // so that e.g. for x = 3 it picks up the = rather than the spaces first.
+
+
+ expr = mb->alias(expr);
+ ExprType t = expressionType(expr);
+
+
+ // See if it is a single qouted character, e.g. 'A'
+ if( expr.left(1) == "\'" && expr.right(1) == "\'" )
+ {
+ if( expr.length() == 3 ) // fall through to report as unknown variable if not of form 'x'
+ {
+ // If so, turn it into a number, and use the ASCII code as the value
+ t = number;
+ expr = QString::number(expr[1].latin1());
+ }
+ }
+
+ // Check for the most common mistake ever!
+ if(expr.contains("="))
+ mistake( Microbe::InvalidEquals );
+ // Check for reserved keywords
+ if(expr=="to"||expr=="step"||expr=="then")
+ mistake( Microbe::ReservedKeyword, expr );
+
+ // Check for empty expressions, or expressions contating spaces
+ // both indicating a Mistake.
+ if(expr.isEmpty())
+ mistake( Microbe::ConsecutiveOperators );
+ else if(expr.contains(QRegExp("\\s")) && t!= extpin)
+ mistake( Microbe::MissingOperator );
+
+ if( t == variable && !mb->isVariableKnown(expr) && !m_pic->isValidPort( expr ) && !m_pic->isValidTris( expr ) )
+ mistake( Microbe::UnknownVariable, expr );
+
+ if ( mb->isVariableKnown(expr) && !mb->variable(expr).isReadable() )
+ mistake( Microbe::WriteOnlyVariable, expr );
+
+ node->setType(t);
+
+ // Since we currently only implement 8 bit unsigned integers, we should disallow
+ // anything outside the range [0-255].
+ if( t == number && !m_bSupressNumberTooBig && (expr.toInt() > 255) )
+ {
+ mistake( Microbe::NumberTooBig );
+ }
+
+ // if there was a pin, we need to decocde it.
+ // For now and sacrificing syntax error checking
+ // we just look for the word "is" then "high" or "low".
+ if( t == extpin )
+ {
+ bool NOT;
+ int i = expr.find("is");
+ if(i > 0)
+ {
+ NOT = expr.contains("low");
+ if(!expr.contains("high") && !expr.contains("low"))
+ mistake( Microbe::HighLowExpected, expr );
+ expr = expr.left(i-1);
+ }
+ else NOT = false;
+ node->setChildOp(NOT?notpin:pin);
+ }
+
+ else if ( t == keypad )
+ node->setChildOp( read_keypad );
+
+ node->setValue(expr);
+}
+
+ExprType Expression::expressionType( const QString & expression )
+{
+ // So we can't handle complex expressions yet anyway,
+ // let's just decide whether it is a variable or number.
+
+ // Thanks to the convention that variable names must not
+ // begin with a number this is extremely simple to do!
+
+ /* But now there is a catch, because there can also be
+ things that have a first character alpha, but are of the form
+ "portb.3 is high", general syntax: portx.n is <high|low>
+ additionally, there can be things that are just porta.6, which just return the truth of that port.
+ In reality it is just:
+ portx.n is high === portx.n
+ portx.n is low === !(portx.n)
+ These types of expression can be identified by the fact
+ that they should be the only things that contain a '.'
+ */
+
+ /* Note that at the moment, literalToInt returns -1 if it is
+ not literal so isLiteral is redundant, but this may change if say
+ negative numbers are implemented
+ */
+
+ int value = Parser::literalToInt(expression);
+ if ( value != -1 )
+ return number;
+
+ if( expression.contains('.') )
+ return extpin;
+
+ if ( mb->variable( expression ).type() == Variable::keypadType )
+ return keypad;
+
+ return variable;
+}
+
+QString Expression::processConstant( const QString & expr, bool * isConstant )
+{
+ bool temp;
+ if (!isConstant)
+ isConstant = &temp;
+
+ QString code;
+
+ // Make a tree to put the expression in.
+ BTreeBase *tree = new BTreeBase();
+ BTreeNode *root = new BTreeNode();
+
+ // parse the expression into the tree
+ buildTree(expr,tree,root,0);
+ // compile the tree into assembly code
+ tree->setRoot(root);
+ tree->pruneTree(tree->root());
+ //code = traverseTree(tree->root());
+ // Look to see if it is a number
+ if( root->type() == number )
+ {
+ code = root->value();
+ *isConstant = true;
+ }
+ else
+ {
+ code = "";
+ *isConstant = false;
+ }
+
+ // Note deleting the tree deletes all nodes, so the root
+ // doesn't need deleting separately.
+ delete tree;
+ return code;
+}
diff --git a/microbe/expression.h b/microbe/expression.h
new file mode 100644
index 0000000..9607f16
--- /dev/null
+++ b/microbe/expression.h
@@ -0,0 +1,126 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef EXPRESSION_H
+#define EXPRESSION_H
+
+#include "microbe.h"
+
+#include <qstring.h>
+
+class PIC14;
+class BTreeNode;
+class Microbe;
+
+/**
+@author Daniel Clarke
+@author David Saxton
+*/
+class Expression
+{
+ public:
+ enum Operation
+ {
+ noop,
+ addition,
+ subtraction,
+ multiplication,
+ division,
+ exponent,
+ equals,
+ notequals,
+ pin,//(returns the truth value obtatined by testing the pin)
+ notpin, //(the result of doing the pin op NOTted).]
+ read_keypad, //read value from keypad
+ function,
+ bwand,
+ bwor,
+ bwxor,
+ bwnot,
+ divbyzero, // used to make handling this situation easier
+ gt,
+ lt,
+ ge,
+ le
+ };
+
+ Expression(PIC14 *pic, Microbe *master, SourceLine sourceLine, bool supressNumberTooBig );
+ ~Expression();
+
+ /**
+ * Generates the code needed to evaluate an expression. Firstly, a tree
+ * is generated from the expression string; then that tree is traversed
+ * to generate the assembly.
+ */
+ void compileExpression( const QString & expression);
+ void compileConditional( const QString & expression, Code * ifCode, Code * elseCode );
+ /**
+ * Returns a *number* rather than evaluating code, and sets isConstant to true
+ * if it the expression evaluated to a constant.
+ */
+ QString processConstant( const QString & expr, bool * isConsant );
+
+ private:
+ PIC14 *m_pic;
+ Microbe *mb;
+
+ /** Turns the operations encoded in the given tree into assembly code */
+ void traverseTree( BTreeNode *root, bool conditionalRoot = false );
+
+ bool isUnaryOp(Operation op);
+
+ void expressionValue( QString expression, BTreeBase *tree, BTreeNode *node );
+ void doOp( Operation op, BTreeNode *left, BTreeNode *right );
+ void doUnaryOp( Operation op, BTreeNode *node );
+ /**
+ * Parses an expression, and generates a tree structure from it.
+ */
+ void buildTree( const QString & expression, BTreeBase *tree, BTreeNode *node, int level );
+
+ static int findSkipBrackets( const QString & expr, char ch, int startPos = 0);
+ static int findSkipBrackets( const QString & expr, QString phrase, int startPos = 0);
+
+ QString stripBrackets( QString expression );
+
+ void mistake( Microbe::MistakeType type, const QString & context = 0 );
+
+ SourceLine m_sourceLine;
+
+ Code * m_ifCode;
+ Code * m_elseCode;
+
+ /**
+ *Returns expression type
+ * 0 = directly usable number (literal)
+ * 1 = variable
+ * 2 = expression that needs evaluating
+ * (maybe not, see enum).
+ */
+ ExprType expressionType( const QString & expression );
+ static bool isLiteral( const QString &text );
+ /**
+ * Normally, only allow numbers upto 255; but for some uses where the
+ * number is not going to be placed in a PIC register (such as when
+ * delaying), we can ignore numbers being too big.
+ */
+ bool m_bSupressNumberTooBig;
+};
+
+#endif
diff --git a/microbe/instruction.cpp b/microbe/instruction.cpp
new file mode 100644
index 0000000..b2b02b4
--- /dev/null
+++ b/microbe/instruction.cpp
@@ -0,0 +1,2309 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke <daniel.jc@gmail.com> *
+ * 2005 by David Saxton <david@bluehaze.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "instruction.h"
+#include "optimizer.h"
+#include "pic14.h"
+
+#include <kdebug.h>
+#include <qstringlist.h>
+
+#include <assert.h>
+#include <iostream>
+using namespace std;
+
+
+//BEGIN class Register
+Register::Register( Type type )
+{
+ m_type = type;
+
+ switch ( m_type )
+ {
+ case TMR0:
+ m_name = "TMR0";
+ break;
+ case OPTION_REG:
+ m_name = "OPTION_REG";
+ break;
+ case PCL:
+ m_name = "PCL";
+ break;
+ case STATUS:
+ m_name = "STATUS";
+ break;
+ case FSR:
+ m_name = "FSR";
+ break;
+ case PORTA:
+ m_name = "PORTA";
+ break;
+ case TRISA:
+ m_name = "TRISA";
+ break;
+ case PORTB:
+ m_name = "PORTB";
+ break;
+ case TRISB:
+ m_name = "TRISB";
+ break;
+ case EEDATA:
+ m_name = "EEDATA";
+ break;
+ case EECON1:
+ m_name = "EECON1";
+ break;
+ case EEADR:
+ m_name = "EEADR";
+ break;
+ case EECON2:
+ m_name = "EECON2";
+ break;
+ case PCLATH:
+ m_name = "PCLATH";
+ break;
+ case INTCON:
+ m_name = "INTCON";
+ break;
+ case WORKING:
+ m_name = "<working>";
+ break;
+ case GPR:
+ case none:
+ break;
+ }
+}
+
+
+Register::Register( const QString & name )
+{
+ m_name = name.stripWhiteSpace();
+ QString upper = m_name.upper();
+
+ if ( upper == "TMR0" )
+ m_type = TMR0;
+ else if ( upper == "OPTION_REG" )
+ m_type = OPTION_REG;
+ else if ( upper == "PCL" )
+ m_type = PCL;
+ else if ( upper == "STATUS")
+ m_type = STATUS;
+ else if ( upper == "FSR")
+ m_type = FSR;
+ else if ( upper == "PORTA")
+ m_type = PORTA;
+ else if ( upper == "TRISA")
+ m_type = TRISA;
+ else if ( upper == "PORTB")
+ m_type = PORTB;
+ else if ( upper == "TRISB")
+ m_type = TRISB;
+ else if ( upper == "EEDATA")
+ m_type = EEDATA;
+ else if ( upper == "EECON1")
+ m_type = EECON1;
+ else if ( upper == "EEADR")
+ m_type = EEADR;
+ else if ( upper == "EECON2")
+ m_type = EECON2;
+ else if ( upper == "PCLATH")
+ m_type = PCLATH;
+ else if ( upper == "INTCON")
+ m_type = INTCON;
+ else
+ m_type = GPR;
+}
+
+
+Register::Register( const char * name )
+{
+ *this = Register( QString(name) );
+}
+
+
+bool Register::operator < ( const Register & reg ) const
+{
+ if ( (type() != GPR) || (reg.type() != GPR) )
+ return type() < reg.type();
+
+ return name() < reg.name();
+}
+
+
+bool Register::operator == ( const Register & reg ) const
+{
+ if ( type() != reg.type() )
+ return false;
+
+ return name() == reg.name();
+}
+
+
+uchar Register::banks() const
+{
+ switch ( m_type )
+ {
+ case TMR0: return Bank0;
+ case OPTION_REG: return Bank1;
+ case PCL: return Bank0 | Bank1;
+ case STATUS: return Bank0 | Bank1;
+ case FSR: return Bank0 | Bank1;
+ case PORTA: return Bank0;
+ case TRISA: return Bank1;
+ case PORTB: return Bank0;
+ case TRISB: return Bank1;
+ case EEDATA: return Bank0;
+ case EECON1: return Bank1;
+ case EEADR: return Bank0;
+ case EECON2: return Bank1;
+ case PCLATH: return Bank0 | Bank1;
+ case INTCON: return Bank0 | Bank1;
+
+ case GPR: return Bank0 | Bank1;
+ case WORKING: return Bank0 | Bank1;
+ case none: return Bank0 | Bank1;
+ }
+
+ return Bank0 | Bank1; // Vacously true (and useful too) - a non-existent bank can be accessed anywhere
+}
+
+
+bool Register::bankDependent() const
+{
+ return ( banks() != (Bank0 | Bank1) );
+}
+
+
+bool Register::affectsExternal() const
+{
+ switch ( m_type )
+ {
+ case PORTA:
+ case TRISA:
+ case PORTB:
+ case TRISB:
+ return true;
+
+ case TMR0:
+ case OPTION_REG:
+ case PCL:
+ case STATUS:
+ case FSR:
+ case EEDATA:
+ case EECON1:
+ case EEADR:
+ case EECON2:
+ case PCLATH:
+ case INTCON:
+ case GPR:
+ case WORKING:
+ case none:
+ return false;
+ }
+ return false;
+}
+//END class Register
+
+
+
+//BEGIN class RegisterBit
+RegisterBit::RegisterBit( uchar bitPos, Register::Type reg )
+{
+ m_bitPos = bitPos;
+ m_registerType = reg;
+
+ switch ( m_registerType )
+ {
+ case Register::STATUS:
+ {
+ switch ( m_bitPos )
+ {
+ case 0: m_name = "C"; break;
+ case 1: m_name = "DC"; break;
+ case 2: m_name = "Z"; break;
+ case 3: m_name = "NOT_PD"; break;
+ case 4: m_name = "NOT_TO"; break;
+ case 5: m_name = "RP0"; break;
+ case 6: m_name = "RP1"; break;
+ case 7: m_name = "IRP"; break;
+ }
+ break;
+ }
+ case Register::INTCON:
+ {
+ switch ( m_bitPos )
+ {
+ case 0: m_name = "RBIF"; break;
+ case 1: m_name = "INTF"; break;
+ case 2: m_name = "T0IF"; break;
+ case 3: m_name = "RBIE"; break;
+ case 4: m_name = "INTE"; break;
+ case 5: m_name = "T0IE"; break;
+ case 6: m_name = "EEIE"; break;
+ case 7: m_name = "GIE"; break;
+ }
+ break;
+ }
+ case Register::OPTION_REG:
+ {
+ switch ( m_bitPos )
+ {
+ case 0: m_name = "PS0"; break;
+ case 1: m_name = "PS1"; break;
+ case 2: m_name = "PS2"; break;
+ case 3: m_name = "PSa"; break;
+ case 4: m_name = "T0SE"; break;
+ case 5: m_name = "T0CS"; break;
+ case 6: m_name = "INTEDG"; break;
+ case 7: m_name = "NOT_RBPU"; break;
+ }
+ break;
+ }
+ case Register::EECON1:
+ {
+ switch ( m_bitPos )
+ {
+ case 0: m_name = "RD"; break;
+ case 1: m_name = "WR"; break;
+ case 2: m_name = "WREN"; break;
+ case 3: m_name = "WRERR"; break;
+ case 4: m_name = "EEIF"; break;
+ }
+ break;
+ }
+
+ case Register::TMR0:
+ case Register::PCL:
+ case Register::FSR:
+ case Register::PORTA:
+ case Register::TRISA:
+ case Register::PORTB:
+ case Register::TRISB:
+ case Register::EEDATA:
+ case Register::EEADR:
+ case Register::EECON2:
+ case Register::PCLATH:
+ case Register::GPR:
+ case Register::WORKING:
+ case Register::none:
+ {
+// kdError() << k_funcinfo << "Bad register: " << reg << endl;
+ }
+ }
+}
+
+
+RegisterBit::RegisterBit( const QString & name )
+{
+ m_name = name.upper().stripWhiteSpace();
+ initFromName();
+}
+
+
+RegisterBit::RegisterBit( const char * name )
+{
+ m_name = QString(name).upper().stripWhiteSpace();
+ initFromName();
+}
+
+
+void RegisterBit::initFromName()
+{
+ bool ok;
+ m_bitPos = m_name.toInt( & ok, 0 );
+ if ( ok )
+ m_registerType = Register::none; // hmm it should be unknown - not none.
+
+ else if ( m_name == "C" )
+ {
+ m_registerType = Register::STATUS;
+ m_bitPos = 0;
+ }
+ else if ( m_name == "DC" )
+ {
+ m_registerType = Register::STATUS;
+ m_bitPos = 1;
+ }
+ else if ( m_name == "Z" )
+ {
+ m_registerType = Register::STATUS;
+ m_bitPos = 2;
+ }
+ else if ( m_name == "NOT_PD" )
+ {
+ m_registerType = Register::STATUS;
+ m_bitPos = 3;
+ }
+ else if ( m_name == "NOT_TO" )
+ {
+ m_registerType = Register::STATUS;
+ m_bitPos = 4;
+ }
+ else if ( m_name == "RP0" )
+ {
+ m_registerType = Register::STATUS;
+ m_bitPos = 5;
+ }
+ else if ( m_name == "RP1" )
+ {
+ m_registerType = Register::STATUS;
+ m_bitPos = 6;
+ }
+ else if ( m_name == "IRP" )
+ {
+ m_registerType = Register::STATUS;
+ m_bitPos = 7;
+ }
+ else if ( m_name == "RBIF" )
+ {
+ m_registerType = Register::INTCON;
+ m_bitPos = 0;
+ }
+ else if ( m_name == "INTF" )
+ {
+ m_registerType = Register::INTCON;
+ m_bitPos = 1;
+ }
+ else if ( m_name == "T0IF" )
+ {
+ m_registerType = Register::INTCON;
+ m_bitPos = 2;
+ }
+ else if ( m_name == "RBIE" )
+ {
+ m_registerType = Register::INTCON;
+ m_bitPos = 3;
+ }
+ else if ( m_name == "INTE" )
+ {
+ m_registerType = Register::INTCON;
+ m_bitPos = 4;
+ }
+ else if ( m_name == "T0IE" )
+ {
+ m_registerType = Register::INTCON;
+ m_bitPos = 5;
+ }
+ else if ( m_name == "EEIE" )
+ {
+ m_registerType = Register::INTCON;
+ m_bitPos = 6;
+ }
+ else if ( m_name == "GIE" )
+ {
+ m_registerType = Register::INTCON;
+ m_bitPos = 7;
+ }
+ else if ( m_name == "PS0" )
+ {
+ m_registerType = Register::OPTION_REG;
+ m_bitPos = 0;
+ }
+ else if ( m_name == "PS1" )
+ {
+ m_registerType = Register::OPTION_REG;
+ m_bitPos = 1;
+ }
+ else if ( m_name == "PS2" )
+ {
+ m_registerType = Register::OPTION_REG;
+ m_bitPos = 2;
+ }
+ else if ( m_name == "PSA" )
+ {
+ m_registerType = Register::OPTION_REG;
+ m_bitPos = 3;
+ }
+ else if ( m_name == "T0SE" )
+ {
+ m_registerType = Register::OPTION_REG;
+ m_bitPos = 4;
+ }
+ else if ( m_name == "T0CS" )
+ {
+ m_registerType = Register::OPTION_REG;
+ m_bitPos = 5;
+ }
+ else if ( m_name == "INTEDG" )
+ {
+ m_registerType = Register::OPTION_REG;
+ m_bitPos = 6;
+ }
+ else if ( m_name == "NOT_RBPU" )
+ {
+ m_registerType = Register::OPTION_REG;
+ m_bitPos = 7;
+ }
+ else if ( m_name == "RD" )
+ {
+ m_registerType = Register::EECON1;
+ m_bitPos = 0;
+ }
+ else if ( m_name == "WR" )
+ {
+ m_registerType = Register::EECON1;
+ m_bitPos = 1;
+ }
+ else if ( m_name == "WREN" )
+ {
+ m_registerType = Register::EECON1;
+ m_bitPos = 2;
+ }
+ else if ( m_name == "WRERR" )
+ {
+ m_registerType = Register::EECON1;
+ m_bitPos = 3;
+ }
+ else if ( m_name == "EEIF" )
+ {
+ m_registerType = Register::EECON1;
+ m_bitPos = 4;
+ }
+ else
+ {
+ m_registerType = Register::none;
+ m_bitPos = 0;
+ kdError() << k_funcinfo << "Unknown bit: " << m_name << endl;
+ }
+}
+//END class RegisterBit
+
+
+
+
+//BEGIN class RegisterState
+RegisterState::RegisterState()
+{
+ reset();
+}
+
+
+void RegisterState::reset()
+{
+ known = 0x0;
+ value = 0x0;
+}
+
+
+void RegisterState::merge( const RegisterState & state )
+{
+ known &= state.known;
+ known &= ~( value ^ state.value );
+}
+
+
+bool RegisterState::operator == ( const RegisterState & state ) const
+{
+ return (known == state.known) && (value == state.value);
+}
+
+
+void RegisterState::print()
+{
+ cout << " known="<<binary(known)<<endl;
+ cout << " value="<<binary(value)<<endl;
+}
+//END class RegisterState
+
+
+
+//BEGIN class RegisterBehaviour
+RegisterBehaviour::RegisterBehaviour()
+{
+ reset();
+}
+
+
+void RegisterBehaviour::reset()
+{
+ depends = 0x0;
+ indep = 0x0;
+}
+//END class RegisterBehaviour
+
+
+
+//BEGIN class ProcessorState
+ProcessorState::ProcessorState()
+{
+}
+
+
+void ProcessorState::reset()
+{
+ working.reset();
+ status.reset();
+
+ RegisterMap::iterator end = m_registers.end();
+ for ( RegisterMap::iterator it = m_registers.begin(); it != end; ++it )
+ (*it).reset();
+}
+
+
+void ProcessorState::merge( const ProcessorState & state )
+{
+ working.merge( state.working );
+ status.merge( state.status );
+
+ RegisterMap::iterator this_it = m_registers.begin();
+ RegisterMap::const_iterator other_it = state.m_registers.begin();
+
+ RegisterMap::iterator this_end = m_registers.end();
+ RegisterMap::const_iterator other_end = state.m_registers.end();
+
+ while ( true )
+ {
+ if ( this_it == this_end )
+ {
+ // So remaining registers of this are default
+ while ( other_it != other_end )
+ {
+ m_registers[ other_it.key() ].merge( *other_it );
+ ++other_it;
+ }
+ return;
+ }
+
+ if ( other_it == other_end )
+ {
+ // So remaining registers of other are default
+ while ( this_it != this_end )
+ {
+ (*this_it).merge( RegisterState() );
+ ++this_it;
+ }
+ return;
+ }
+
+ RegisterState thisReg = *this_it;
+ RegisterState otherReg = *other_it;
+
+ if ( this_it.key() == other_it.key() )
+ {
+ (*this_it).merge( *other_it );
+ ++this_it;
+ ++other_it;
+ }
+ else if ( this_it.key() < other_it.key() )
+ {
+ (*this_it).merge( RegisterState() );
+ ++this_it;
+ }
+ else // other_it.key() < this_it.key()
+ {
+ m_registers[ other_it.key() ].merge( *other_it );
+ ++other_it;
+ }
+ }
+}
+
+
+RegisterState & ProcessorState::reg( const Register & reg )
+{
+ if ( reg.type() == Register::WORKING )
+ return working;
+
+ if ( reg.type() == Register::STATUS )
+ return status;
+
+ return m_registers[ reg ];
+}
+
+
+RegisterState ProcessorState::reg( const Register & reg ) const
+{
+ if ( reg.type() == Register::WORKING )
+ return working;
+
+ if ( reg.type() == Register::STATUS )
+ return status;
+
+ return m_registers[ reg ];
+}
+
+
+bool ProcessorState::operator == ( const ProcessorState & state ) const
+{
+ if ( working != state.working )
+ return false;
+
+ if ( status != state.status )
+ return false;
+
+ RegisterMap::const_iterator this_it = m_registers.begin();
+ RegisterMap::const_iterator other_it = state.m_registers.begin();
+
+ RegisterMap::const_iterator this_end = m_registers.end();
+ RegisterMap::const_iterator other_end = state.m_registers.end();
+
+ while ( true )
+ {
+ if ( this_it == this_end )
+ {
+ // So remaining registers of this are default
+ while ( other_it != other_end )
+ {
+ if ( *other_it != RegisterState() )
+ return false;
+ ++other_it;
+ }
+ return true;
+ }
+
+ if ( other_it == other_end )
+ {
+ // So remaining registers of other are default
+ while ( this_it != this_end )
+ {
+ if ( *this_it != RegisterState() )
+ return false;
+ ++this_it;
+ }
+ return true;
+ }
+
+ RegisterState thisReg = *this_it;
+ RegisterState otherReg = *other_it;
+
+ if ( this_it.key() == other_it.key() )
+ {
+ if ( *this_it != *other_it )
+ return false;
+ ++this_it;
+ ++other_it;
+ }
+ else if ( this_it.key() < other_it.key() )
+ {
+ if ( *this_it != RegisterState() )
+ return false;
+ ++this_it;
+ }
+ else // other_it.key() < this_it.key()
+ {
+ if ( *other_it != RegisterState() )
+ return false;
+ ++other_it;
+ }
+ }
+}
+
+
+void ProcessorState::print()
+{
+ cout << " WORKING:\n";
+ working.print();
+ cout << " STATUS:\n";
+ working.print();
+ RegisterMap::iterator end = m_registers.end();
+ for ( RegisterMap::iterator it = m_registers.begin(); it != end; ++it )
+ {
+ cout << " " << it.key().name() << ":\n";
+ it.data().print();
+ }
+}
+//END class ProcessorState
+
+
+
+//BEGIN class ProcessorBehaviour
+ProcessorBehaviour::ProcessorBehaviour()
+{
+}
+
+
+void ProcessorBehaviour::reset()
+{
+ working.reset();
+ status.reset();
+
+ RegisterMap::iterator end = m_registers.end();
+ for ( RegisterMap::iterator it = m_registers.begin(); it != end; ++it )
+ (*it).reset();
+}
+
+
+RegisterBehaviour & ProcessorBehaviour::reg( const Register & reg )
+{
+ if ( reg.type() == Register::WORKING )
+ return working;
+
+ if ( reg.type() == Register::STATUS )
+ return status;
+
+ return m_registers[ reg ];
+}
+//END class ProcessorBehaviour
+
+
+
+//BEGIN class RegisterDepends
+RegisterDepends::RegisterDepends()
+{
+ reset();
+}
+
+
+void RegisterDepends::reset()
+{
+ working = 0x0;
+ status = 0x0;
+
+ RegisterMap::iterator end = m_registers.end();
+ for ( RegisterMap::iterator it = m_registers.begin(); it != end; ++it )
+ (*it) = 0x0;
+}
+
+
+uchar & RegisterDepends::reg( const Register & reg )
+{
+ if ( reg.type() == Register::WORKING )
+ return working;
+
+ if ( reg.type() == Register::STATUS )
+ return status;
+
+ // If we don't already have the register, we need to reset it first
+ if ( !m_registers.contains( reg.name() ) )
+ m_registers[ reg ] = 0xff;
+
+ return m_registers[ reg ];
+}
+//END class RegisterDepends
+
+
+
+//BEGIN clas Code
+Code::Code()
+{
+}
+
+
+void Code::merge( Code * code, InstructionPosition middleInsertionPosition )
+{
+ if ( code == this )
+ {
+ cout << k_funcinfo << "identical\n";
+ return;
+ }
+
+ if ( !code )
+ return;
+
+ // Reparent instructions
+ for ( unsigned i = 0; i < PositionCount; ++i )
+ {
+ InstructionList * list = code->instructionList( (InstructionPosition)i );
+ InstructionList::const_iterator end = list->end();
+ for ( InstructionList::const_iterator it = list->begin(); it != end; ++it )
+ append( *it, ( (i == Middle) ? middleInsertionPosition : (InstructionPosition)i ) );
+
+ // Queue any labels that the other code has queued
+ m_queuedLabels[i] += code->queuedLabels( (InstructionPosition)i );
+ }
+}
+
+
+void Code::queueLabel( const QString & label, InstructionPosition position )
+{
+// cout << k_funcinfo << "label="<<label<<" position="<<position<<'\n';
+ m_queuedLabels[ position ] << label;
+}
+
+
+void Code::removeInstruction( Instruction * instruction )
+{
+ if ( !instruction )
+ return;
+
+ // If the instruction could potentially be jumped over by a BTFSS or a
+ // BTFSC intsruction, then we must also remove the bit test instruction,
+ // else the next instruction will be jumped over instead after removal.
+ // Removing the bit test instruction is perfectly safe as it only does
+ // branching (not setting of any bits, etc).
+
+ // Any labels that the instruction has must be given to the next
+ // instruction.
+
+ iterator e = end();
+ iterator previous = e; // Refers to the previous instruction if it was a bit test instruction
+ for ( iterator i = begin(); i != e; ++i )
+ {
+ if ( *i != instruction )
+ {
+ if ( dynamic_cast<Instr_btfss*>(*i) || dynamic_cast<Instr_btfsc*>(*i) )
+ previous = i;
+ else
+ previous = e;
+ continue;
+ }
+
+ iterator next = ++iterator(i);
+
+ QStringList labels = instruction->labels();
+ i.list->remove( i.it );
+
+ if ( previous != e )
+ {
+ labels += (*previous)->labels();
+ previous.list->remove( previous.it );
+ }
+
+ if ( next != e )
+ (*next)->addLabels( labels );
+
+ break;
+ }
+
+// instruction->removeOutputs();
+}
+
+
+void Code::append( Instruction * instruction, InstructionPosition position )
+{
+ if ( !instruction )
+ return;
+
+// cout << k_funcinfo << instruction->code() << '\n';
+
+ removeInstruction( instruction );
+ m_instructionLists[position].append( instruction );
+
+ instruction->setCode( this );
+
+ if ( instruction->type() == Instruction::Assembly /*||
+ instruction->type() == Instruction::Raw*/ )
+ {
+// if ( (position == Middle) && !m_queuedLabels[position].isEmpty() )
+// cout << "adding queued labels for 1: " << m_queuedLabels[position].join(",") << '\n';
+ instruction->addLabels( m_queuedLabels[position] );
+ m_queuedLabels[position].clear();
+ }
+}
+
+
+Instruction * Code::instruction( const QString & label ) const
+{
+ for ( unsigned i = 0; i < PositionCount; ++i )
+ {
+ InstructionList::const_iterator end = m_instructionLists[i].end();
+ for ( InstructionList::const_iterator it = m_instructionLists[i].begin(); it != end; ++it )
+ {
+ if ( (*it)->labels().contains( label ) )
+ return *it;
+ }
+ }
+ return 0l;
+}
+
+
+Code::iterator Code::find( Instruction * instruction )
+{
+ iterator e = end();
+ iterator i = begin();
+ for ( ; i != e; ++i )
+ {
+ if ( *i == instruction )
+ break;
+ }
+ return i;
+}
+
+
+void Code::postCompileConstruct()
+{
+ // Give any queued labels to the instructions in the subsequent code block
+ for ( unsigned i = 0; i < PositionCount; ++i )
+ {
+ if ( m_queuedLabels[i].isEmpty() )
+ continue;
+
+ QStringList labels = m_queuedLabels[i];
+ m_queuedLabels[i].clear();
+
+ // Find an instruction to dump them onto
+ for ( unsigned block = i+1; block < PositionCount; ++block )
+ {
+ bool added = false;
+
+ InstructionList::iterator end = m_instructionLists[block].end();
+ for ( InstructionList::iterator it = m_instructionLists[block].begin(); it != end; ++it )
+ {
+ if ( (*it)->type() == Instruction::Assembly )
+ {
+ (*it)->addLabels( labels );
+ added = true;
+ break;
+ }
+ }
+
+ if ( added )
+ break;
+ }
+ }
+}
+
+
+QString Code::generateCode( PIC14 * pic ) const
+{
+ QString code;
+
+ const QStringList variables = findVariables();
+ if ( !variables.isEmpty() )
+ {
+ code += "; Variables\n";
+ uchar reg = pic->gprStart();
+ QStringList::const_iterator end = variables.end();
+ for ( QStringList::const_iterator it = variables.begin(); it != end; ++it )
+ code += QString("%1\tequ\t0x%2\n").arg( *it ).arg( QString::number( reg++, 16 ) );
+
+ code += "\n";
+ }
+
+ QString picString = pic->minimalTypeString();
+ code += QString("list p=%1\n").arg( picString );
+ code += QString("include \"p%2.inc\"\n\n").arg( picString.lower() );
+
+ code += "; Config options\n";
+ code += " __config _WDT_OFF\n\n";
+
+ code += "START\n\n";
+
+ for ( unsigned i = 0; i < PositionCount; ++i )
+ {
+ InstructionList::const_iterator end = m_instructionLists[i].end();
+ for ( InstructionList::const_iterator it = m_instructionLists[i].begin(); it != end; ++it )
+ {
+ const QStringList labels = (*it)->labels();
+ if ( !labels.isEmpty() )
+ {
+ code += '\n';
+ QStringList::const_iterator labelsEnd = labels.end();
+ for ( QStringList::const_iterator labelsIt = labels.begin(); labelsIt != labelsEnd; ++labelsIt )
+ code += *labelsIt + '\n';
+ }
+
+ if ( (*it)->type() == Instruction::Assembly )
+ code += '\t';
+ code += (*it)->code() + '\n';
+ }
+ }
+
+ return code;
+}
+
+
+QStringList Code::findVariables() const
+{
+ QStringList variables;
+
+ const_iterator e = end();
+ for ( const_iterator i = begin(); i != e; ++i )
+ {
+ if ( (*i)->file().type() != Register::GPR )
+ continue;
+
+ QString alias = (*i)->file().name();
+ if ( !variables.contains( alias ) )
+ variables << alias;
+ }
+
+ return variables;
+}
+
+
+void Code::generateLinksAndStates()
+{
+ CodeIterator e = end();
+
+ for ( CodeIterator it = begin(); it != e; ++it )
+ (*it)->clearLinks();
+
+ for ( CodeIterator it = begin(); it != e; ++it )
+ (*it)->generateLinksAndStates( it );
+
+ // Generate return links for call instructions
+ // This cannot be done from the call instructions as we need to have
+ // generated the links first.
+ for ( CodeIterator it = begin(); it != e; ++it )
+ {
+ Instr_call * ins = dynamic_cast<Instr_call*>(*it);
+ if ( !ins )
+ continue;
+
+ Instruction * next = *(++Code::iterator(it));
+ ins->makeReturnLinks( next );
+ }
+}
+
+
+void Code::setAllUnused()
+{
+ CodeIterator e = end();
+ for ( CodeIterator it = begin(); it != e; ++it )
+ {
+ (*it)->setUsed( false );
+ (*it)->resetRegisterDepends();
+ }
+}
+
+
+CodeIterator Code::begin()
+{
+ // Following code is very similar to the version of this function.
+ // Make sure any changes are applied to both (when applicable).
+
+ for ( unsigned i = 0; i < PositionCount; ++i )
+ {
+ if ( m_instructionLists[i].isEmpty() )
+ continue;
+
+ CodeIterator codeIterator;
+ codeIterator.code = this;
+ codeIterator.it = m_instructionLists[i].begin();
+ codeIterator.pos = (Code::InstructionPosition)i;
+ codeIterator.list = & m_instructionLists[i];
+ codeIterator.listEnd = m_instructionLists[i].end();
+
+ return codeIterator;
+ }
+
+ return end();
+}
+
+
+CodeIterator Code::end()
+{
+ // Following code is very similar to the version of this function.
+ // Make sure any changes are applied to both (when applicable).
+
+ CodeIterator codeIterator;
+ codeIterator.code = this;
+ codeIterator.it = m_instructionLists[ PositionCount - 1 ].end();
+ codeIterator.pos = (Code::InstructionPosition)(Code::PositionCount - 1);
+ codeIterator.list = & m_instructionLists[ PositionCount - 1 ];
+ codeIterator.listEnd = m_instructionLists[ PositionCount - 1 ].end();
+ return codeIterator;
+}
+
+
+CodeConstIterator Code::begin() const
+{
+ // Following code is very similar to the non-const version of this function.
+ // Make sure any changes are applied to both (when applicable).
+
+ for ( unsigned i = 0; i < PositionCount; ++i )
+ {
+ if ( m_instructionLists[i].isEmpty() )
+ continue;
+
+ CodeConstIterator codeIterator;
+ codeIterator.code = this;
+ codeIterator.it = m_instructionLists[i].begin();
+ codeIterator.pos = (Code::InstructionPosition)i;
+ codeIterator.list = & m_instructionLists[i];
+ codeIterator.listEnd = m_instructionLists[i].end();
+
+ return codeIterator;
+ }
+
+ return end();
+}
+
+
+CodeConstIterator Code::end() const
+{
+ // Following code is very similar to the non-const version of this function.
+ // Make sure any changes are applied to both (when applicable).
+
+ CodeConstIterator codeIterator;
+ codeIterator.code = this;
+ codeIterator.it = m_instructionLists[ PositionCount - 1 ].end();
+ codeIterator.pos = (Code::InstructionPosition)(Code::PositionCount - 1);
+ codeIterator.list = & m_instructionLists[ PositionCount - 1 ];
+ codeIterator.listEnd = m_instructionLists[ PositionCount - 1 ].end();
+ return codeIterator;
+}
+//END class Code
+
+
+
+//BEGIN class CodeIterator
+CodeIterator & CodeIterator::operator ++ ()
+{
+ // NOTE: This code is very similar to the const version.
+ // Any changes to thsi code should be applied there as well (when applicable).
+
+ do
+ {
+ if ( ++it == listEnd && pos < (Code::PositionCount - 1) )
+ {
+ bool found = false;
+ for ( pos = (Code::InstructionPosition)(pos+1); pos < Code::PositionCount; pos = (Code::InstructionPosition)(pos+1) )
+ {
+ list = code->instructionList( pos );
+ listEnd = list->end();
+ if ( list->isEmpty() )
+ continue;
+
+ it = list->begin();
+ found = true;
+ break;
+ }
+
+ if ( !found )
+ it = listEnd;
+ }
+ }
+ while ( (it != listEnd) && ((*it)->type() != Instruction::Assembly) );
+
+ return *this;
+}
+
+
+CodeIterator & CodeIterator::removeAndIncrement()
+{
+ Instruction * i = *it;
+ ++(*this);
+ code->removeInstruction( i );
+ return *this;
+}
+
+
+void CodeIterator::insertBefore( Instruction * ins )
+{
+ list->insert( it, ins );
+}
+//END class CodeIterator
+
+
+
+//BEGIN class CodeConstIterator
+CodeConstIterator & CodeConstIterator::operator ++ ()
+{
+ // NOTE: This code is very similar to the non-const version.
+ // Any changes to thsi code should be applied there as well (when applicable).
+
+ do
+ {
+ if ( ++it == listEnd && pos < (Code::PositionCount - 1) )
+ {
+ bool found = false;
+ for ( pos = (Code::InstructionPosition)(pos+1); pos < Code::PositionCount; pos = (Code::InstructionPosition)(pos+1) )
+ {
+ list = code->instructionList( pos );
+ listEnd = list->end();
+ if ( list->isEmpty() )
+ continue;
+
+ it = list->begin();
+ found = true;
+ break;
+ }
+
+ if ( !found )
+ it = listEnd;
+ }
+ }
+ while ( (it != listEnd) && ((*it)->type() != Instruction::Assembly) );
+
+ return *this;
+}
+//END class CodeConstIterator
+
+
+
+
+//BEGIN class Instruction
+Instruction::Instruction()
+{
+ m_bInputStateChanged = true;
+ m_bPositionAffectsBranching = false;
+ m_bUsed = false;
+ m_literal = 0;
+ m_dest = 0;
+}
+
+
+Instruction::~ Instruction()
+{
+}
+
+
+void Instruction::addLabels( const QStringList & labels )
+{
+ m_labels += labels;
+}
+
+
+void Instruction::setLabels( const QStringList & labels )
+{
+ m_labels = labels;
+}
+
+
+void Instruction::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+ m_outputState.reset();
+}
+
+
+ProcessorBehaviour Instruction::behaviour() const
+{
+ return ProcessorBehaviour();
+}
+
+
+void Instruction::makeOutputLinks( Code::iterator current, bool firstOutput, bool secondOutput )
+{
+ if ( !firstOutput && !secondOutput )
+ return;
+
+ ++current;
+ if ( !*current )
+ {
+ kdWarning() << k_funcinfo << "current+1 is null"<<endl;
+ return;
+ }
+ if ( firstOutput )
+ (*current)->addInputLink( this );
+
+ if ( !secondOutput )
+ return;
+
+ ++current;
+ (*current)->addInputLink( this );
+}
+
+
+void Instruction::makeLabelOutputLink( const QString & label )
+{
+ Instruction * output = m_pCode->instruction( label );
+ if ( output )
+ output->addInputLink( this );
+}
+
+
+void Instruction::addInputLink( Instruction * instruction )
+{
+ // Don't forget that a link to ourself is valid!
+ if ( !instruction || m_inputLinks.contains( instruction ) )
+ return;
+
+ m_inputLinks << instruction;
+ instruction->addOutputLink( this );
+}
+
+
+void Instruction::addOutputLink( Instruction * instruction )
+{
+ // Don't forget that a link to ourself is valid!
+ if ( !instruction || m_outputLinks.contains( instruction ) )
+ return;
+
+ m_outputLinks << instruction;
+ instruction->addInputLink( this );
+}
+
+
+void Instruction::removeInputLink( Instruction * instruction )
+{
+ m_inputLinks.remove( instruction );
+}
+
+
+void Instruction::removeOutputLink( Instruction * instruction )
+{
+ m_outputLinks.remove( instruction );
+}
+
+
+void Instruction::clearLinks()
+{
+ m_inputLinks.clear();
+ m_outputLinks.clear();
+}
+//END class Instruction
+
+
+
+//BEGIN Byte-Oriented File Register Operations
+QString Instr_addwf::code() const
+{
+ return QString("addwf\t%1,%2").arg( m_file.name() ).arg( m_dest );
+}
+
+void Instr_addwf::generateLinksAndStates( Code::iterator current )
+{
+ m_outputState = m_inputState;
+
+ m_outputState.reg( outputReg() ).value = (m_inputState.working.value + m_inputState.reg( m_file ).value) & 0xff;
+ m_outputState.reg( outputReg() ).known = ((m_inputState.working.known == 0xff) && (m_inputState.reg( m_file ).known == 0xff)) ? 0xff : 0x0;
+
+ m_outputState.status.known &= ~( (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z) );
+
+ if ( m_file.type() != Register::PCL || m_dest == 0 )
+ {
+ makeOutputLinks( current );
+ return;
+ }
+
+ ++current; // Don't have a link to ourself
+
+ // maxInc is the greatest possibly value that we might have incremented the program counter by.
+ // It is generated by ORing the known bits of the working register with the greatest value
+ // of the unknown bits;
+ uchar maxInc = m_inputState.working.maxValue();
+ if ( maxInc < 0xff )
+ maxInc++;
+// cout << "m_inputState.working.known="<<int(m_inputState.working.known)<<" maxInc="<<int(maxInc)<<'\n';
+ Code::iterator end = m_pCode->end();
+ for ( int i = 0; current != end && i < maxInc; ++i, ++current )
+ {
+ (*current)->addInputLink( this );
+// if ( i != maxInc-1 )
+// (*current)->setPositionAffectsBranching( true );
+ }
+}
+
+ProcessorBehaviour Instr_addwf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ // Depend on W and f
+ behaviour.working.depends = 0xff;
+ behaviour.reg( m_file ).depends = 0xff;
+
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+ behaviour.status.indep = (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z);
+ return behaviour;
+}
+
+
+
+QString Instr_andwf::code() const
+{
+ return QString("andwf\t%1,%2").arg( m_file.name() ).arg( m_dest );
+}
+
+void Instr_andwf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+ m_outputState = m_inputState;
+
+ uchar definiteOnes = m_inputState.reg( m_file ).definiteOnes() & m_outputState.working.definiteOnes();
+ m_outputState.reg( outputReg() ).value = definiteOnes;
+ m_outputState.reg( outputReg() ).known = m_inputState.reg( m_file ).definiteZeros() | m_inputState.working.definiteZeros() | definiteOnes;
+
+ m_outputState.status.known &= ~(1 << RegisterBit::Z);
+}
+
+ProcessorBehaviour Instr_andwf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ // Depend on W and f
+ behaviour.working.depends = 0xff;
+ behaviour.reg( m_file ).depends = 0xff;
+
+ if ( m_dest == 0 )
+ behaviour.working.indep = m_inputState.reg( m_file ).known & ~( m_inputState.reg( m_file ).value);
+
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+ behaviour.status.indep = (1 << RegisterBit::Z);
+ return behaviour;
+}
+
+
+QString Instr_clrf::code() const
+{
+ return QString("clrf\t%1").arg( m_file.name() );
+}
+
+void Instr_clrf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.reg( m_file ).known = 0xff;
+ m_outputState.reg( m_file ).value = 0x0;
+
+ m_outputState.status.known |= (1 << RegisterBit::Z);
+ m_outputState.status.value |= (1 << RegisterBit::Z);
+}
+
+ProcessorBehaviour Instr_clrf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ behaviour.reg( m_file ).indep = 0xff;
+
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+ behaviour.status.indep = (1 << RegisterBit::Z);
+
+ return behaviour;
+}
+
+
+//TODO CLRW
+//TODO COMF
+
+
+QString Instr_decf::code() const
+{
+ return QString("decf\t%1,%2").arg( m_file.name() ).arg( m_dest );
+}
+
+void Instr_decf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.status.known &= ~(1 << RegisterBit::Z);
+
+ m_outputState.reg( outputReg() ).known = 0x0;
+}
+
+ProcessorBehaviour Instr_decf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ behaviour.reg( m_file ).depends = 0xff;
+
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+ behaviour.status.indep = (1 << RegisterBit::Z);
+
+ return behaviour;
+}
+
+
+QString Instr_decfsz::code() const
+{
+ return QString("decfsz\t%1,%2").arg( m_file.name() ).arg( m_dest );
+}
+
+void Instr_decfsz::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current, true, true );
+
+ m_outputState = m_inputState;
+ m_outputState.reg( outputReg() ).known = 0x0;
+}
+
+ProcessorBehaviour Instr_decfsz::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ behaviour.reg( m_file ).depends = 0xff;
+
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+ return behaviour;
+}
+
+
+QString Instr_incf::code() const
+{
+ return QString("incf\t%1,%2").arg( m_file.name() ).arg( m_dest );
+}
+
+void Instr_incf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.status.known &= ~(1 << RegisterBit::Z);
+
+ m_outputState.reg( outputReg() ).known = 0x0;
+}
+
+ProcessorBehaviour Instr_incf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ behaviour.reg( m_file ).depends = 0xff;
+
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+ behaviour.status.indep = (1 << RegisterBit::Z);
+ return behaviour;
+}
+
+
+//TODO INCFSZ
+
+
+QString Instr_iorwf::code() const
+{
+ return QString("iorwf\t%1,%2").arg( m_file.name() ).arg( m_dest );
+}
+
+void Instr_iorwf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.status.known &= ~(1 << RegisterBit::Z);
+
+ m_outputState.reg( outputReg() ).known = 0x0;
+}
+
+ProcessorBehaviour Instr_iorwf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ // Depend on W and f
+ behaviour.working.depends = 0xff;
+ behaviour.reg( m_file ).depends = 0xff;
+
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+ behaviour.status.indep = (1 << RegisterBit::Z);
+ return behaviour;
+}
+
+
+QString Instr_movf::code() const
+{
+ return QString("movf\t%1,%2").arg( m_file.name() ).arg( m_dest );
+}
+
+void Instr_movf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+
+ if ( m_inputState.reg( m_file ).known == 0xff )
+ {
+ m_outputState.status.known |= (1 << RegisterBit::Z);
+ bool isZero = (m_inputState.reg( m_file ).value == 0x0);
+ if ( isZero )
+ m_outputState.status.value |= (1 << RegisterBit::Z);
+ else
+ m_outputState.status.value &= ~(1 << RegisterBit::Z);
+ }
+ else
+ m_outputState.status.known &= ~(1 << RegisterBit::Z);
+
+ if ( m_dest == 0 )
+ {
+ // Writing to the working register
+ m_outputState.working.known = m_inputState.reg( m_file ).known;
+ m_outputState.working.value = m_inputState.reg( m_file ).value;
+ }
+}
+
+ProcessorBehaviour Instr_movf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ if ( m_dest == 0 )
+ behaviour.working.indep = 0xff;
+
+ behaviour.reg( m_file ).depends = 0xff;
+
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+ behaviour.status.indep = (1 << RegisterBit::Z);
+ return behaviour;
+}
+
+
+QString Instr_movwf::code() const
+{
+ return QString("movwf\t%1").arg( m_file.name() );
+}
+
+void Instr_movwf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.reg( m_file ).known = m_inputState.working.known;
+ m_outputState.reg( m_file ).value = m_inputState.working.value;
+}
+
+ProcessorBehaviour Instr_movwf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ behaviour.reg( m_file ).indep = 0xff;
+ behaviour.working.depends = 0xff;
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+
+ return behaviour;
+}
+
+
+//TODO NOP
+
+
+
+QString Instr_rlf::code() const
+{
+ return QString("rlf\t%1,%2").arg( m_file.name() ).arg( m_dest );
+}
+
+void Instr_rlf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.status.known &= ~(1 << RegisterBit::C);
+
+ m_outputState.reg( outputReg() ).known = 0x0;
+}
+
+ProcessorBehaviour Instr_rlf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ // Is the value written to W or f?
+ if ( m_dest == 0 )
+ behaviour.working.indep = 0xff;
+
+ behaviour.reg( m_file ).depends = 0xff;
+
+ behaviour.status.depends = (1 << RegisterBit::C) | (m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0);
+ behaviour.status.indep = (1 << RegisterBit::C);
+ return behaviour;
+}
+
+
+QString Instr_rrf::code() const
+{
+ return QString("rrf\t%1,%2").arg( m_file.name() ).arg( m_dest );
+}
+
+void Instr_rrf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.status.known &= ~(1 << RegisterBit::C);
+
+ m_outputState.reg( outputReg() ).known = 0x0;
+}
+
+ProcessorBehaviour Instr_rrf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ if ( m_dest == 0 )
+ behaviour.working.indep = 0xff;
+
+ behaviour.reg( m_file ).depends = 0xff;
+
+ behaviour.status.depends = (1 << RegisterBit::C) | (m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0);
+ behaviour.status.indep = (1 << RegisterBit::C);
+ return behaviour;
+}
+
+
+QString Instr_subwf::code() const
+{
+ return QString("subwf\t%1,%2").arg( m_file.name() ).arg( m_dest );
+}
+
+void Instr_subwf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+
+ if ( (m_inputState.working.known == 0xff) && (m_inputState.reg( m_file ).known == 0xff) )
+ {
+ m_outputState.reg( outputReg() ).known = 0xff;
+ m_outputState.reg( outputReg() ).value = (m_inputState.reg( m_file ).value - m_inputState.working.value) & 0xff;
+ }
+ else
+ m_outputState.reg( outputReg() ).known = 0x0;
+
+
+ m_outputState.status.known &= ~( (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z) );
+
+ if ( m_inputState.working.minValue() > m_inputState.reg( m_file ).maxValue() )
+ {
+ m_outputState.status.value &= ~(1 << RegisterBit::C);
+ m_outputState.status.known |= (1 << RegisterBit::C);
+ }
+ else if ( m_inputState.working.maxValue() <= m_inputState.reg( m_file ).minValue() )
+ {
+ m_outputState.status.value |= (1 << RegisterBit::C);
+ m_outputState.status.known |= (1 << RegisterBit::C);
+ }
+
+ if ( (m_inputState.working.known == 0xff) && (m_inputState.reg( m_file ).known == 0xff) )
+ {
+ bool isZero = (m_inputState.working.value == m_inputState.reg( m_file ).value);
+ if ( isZero )
+ m_outputState.status.value |= (1 << RegisterBit::Z);
+ else
+ m_outputState.status.value &= ~(1 << RegisterBit::Z);
+ m_outputState.status.known |= (1 << RegisterBit::Z);
+ }
+}
+
+ProcessorBehaviour Instr_subwf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ // Depend on W and f
+ behaviour.working.depends = 0xff;
+ behaviour.reg( m_file ).depends = 0xff;
+
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+ behaviour.status.indep = (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z);
+ return behaviour;
+}
+
+
+QString Instr_swapf::code() const
+{
+ return QString("swapf\t%1,%2").arg( m_file.name() ).arg( m_dest );
+}
+
+void Instr_swapf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ if ( m_dest == 0 )
+ {
+ // Writing to the working register
+ m_outputState.working.known = 0x0;
+ }
+}
+
+ProcessorBehaviour Instr_swapf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+ behaviour.reg( m_file ).depends = 0xff;
+ behaviour.working.indep = ( m_dest == 0 ) ? 0xff : 0x0;
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+ return behaviour;
+}
+
+
+QString Instr_xorwf::code() const
+{
+ return QString("xorwf\t%1,%2").arg( m_file.name() ).arg( m_dest );
+}
+
+void Instr_xorwf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.status.known &= ~(1 << RegisterBit::Z);
+
+ m_outputState.reg( outputReg() ).known = 0x0;
+}
+
+ProcessorBehaviour Instr_xorwf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ // Depend on W and f
+ behaviour.working.depends = 0xff;
+ behaviour.reg( m_file ).depends = 0xff;
+
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+ behaviour.status.indep = (1 << RegisterBit::Z);
+ return behaviour;
+}
+//END Byte-Oriented File Register Operations
+
+
+
+//BEGIN Bit-Oriented File Register Operations
+QString Instr_bcf::code() const
+{
+ return QString("bcf\t\t%1,%2").arg( m_file.name() ).arg( m_bit.name() );
+}
+
+void Instr_bcf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.reg( m_file ).value &= ~uchar(1 << m_bit.bitPos());
+ m_outputState.reg( m_file ).known |= uchar(1 << m_bit.bitPos());
+}
+
+ProcessorBehaviour Instr_bcf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+ behaviour.reg( m_file ).indep = 1 << m_bit.bitPos();
+ return behaviour;
+}
+
+
+QString Instr_bsf::code() const
+{
+ return QString("bsf\t\t%1,%2").arg( m_file.name() ).arg( m_bit.name() );
+}
+
+void Instr_bsf::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.reg( m_file ).value |= uchar(1 << m_bit.bitPos());
+ m_outputState.reg( m_file ).known |= uchar(1 << m_bit.bitPos());
+}
+
+ProcessorBehaviour Instr_bsf::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+ behaviour.status.depends = m_file.bankDependent() ? (1 << RegisterBit::RP0) : 0x0;
+ behaviour.reg( m_file ).indep = 1 << m_bit.bitPos();
+ return behaviour;
+}
+
+
+QString Instr_btfsc::code() const
+{
+ return QString("btfsc\t%1,%2").arg( m_file.name() ).arg( m_bit.name() );
+}
+
+void Instr_btfsc::generateLinksAndStates( Code::iterator current )
+{
+ m_outputState = m_inputState;
+
+ if ( m_inputState.reg( m_file ).known & (1 << m_bit.bitPos()) )
+ {
+ bool bit = m_inputState.reg( m_file ).value & (1 << m_bit.bitPos());
+ makeOutputLinks( current, bit, !bit );
+ }
+ else
+ makeOutputLinks( current, true, true );
+}
+
+ProcessorBehaviour Instr_btfsc::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+ behaviour.reg( m_file ).depends = 1 << m_bit.bitPos();
+ behaviour.status.depends = (m_file.type() == Register::STATUS) ? m_bit.bit() : 0x0;
+ return behaviour;
+}
+
+
+QString Instr_btfss::code() const
+{
+ return QString("btfss\t%1,%2").arg( m_file.name() ).arg( m_bit.name() );
+}
+
+void Instr_btfss::generateLinksAndStates( Code::iterator current )
+{
+ m_outputState = m_inputState;
+
+ if ( m_inputState.reg( m_file ).known & (1 << m_bit.bitPos()) )
+ {
+ bool bit = m_inputState.reg( m_file ).value & (1 << m_bit.bitPos());
+ makeOutputLinks( current, !bit, bit );
+ }
+ else
+ makeOutputLinks( current, true, true );
+}
+
+ProcessorBehaviour Instr_btfss::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+ behaviour.reg( m_file ).depends = 1 << m_bit.bitPos();
+ behaviour.status.depends = (m_file.type() == Register::STATUS) ? m_bit.bit() : 0x0;
+ return behaviour;
+}
+//END Bit-Oriented File Register Operations
+
+
+
+//BEGIN Literal and Control Operations
+QString Instr_addlw::code() const
+{
+ return QString("addlw\t%1").arg( m_literal );
+}
+
+void Instr_addlw::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.working.value = (m_inputState.working.value + m_literal) & 0xff;
+ m_outputState.working.known = (m_inputState.working.known == 0xff) ? 0xff : 0x0;
+ m_outputState.status.known &= ~( (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z) );
+}
+
+ProcessorBehaviour Instr_addlw::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ behaviour.working.depends = 0xff;
+
+ behaviour.status.indep = (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z);
+
+ return behaviour;
+}
+
+
+QString Instr_andlw::code() const
+{
+ return QString("andlw\t%1").arg( m_literal );
+}
+
+void Instr_andlw::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.working.value = (m_inputState.working.value & m_literal) & 0xff;
+ m_outputState.working.known |= ~m_literal; // Now know any bits that are zero in value
+ m_outputState.status.known &= ~(1 << RegisterBit::Z);
+}
+
+ProcessorBehaviour Instr_andlw::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ behaviour.working.indep = ~m_literal;
+ behaviour.working.depends = m_literal;
+
+ behaviour.status.indep = (1 << RegisterBit::Z);
+ return behaviour;
+}
+
+
+QString Instr_call::code() const
+{
+ return QString("call\t%1").arg( m_label );
+}
+
+void Instr_call::generateLinksAndStates( Code::iterator current )
+{
+ (void)current;
+ makeLabelOutputLink( m_label );
+
+ m_outputState = m_inputState;
+}
+
+ProcessorBehaviour Instr_call::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+ return behaviour;
+}
+
+void Instr_call::makeReturnLinks( Instruction * next )
+{
+ m_pCode->setAllUnused();
+ linkReturns( m_pCode->instruction( m_label ), next );
+}
+
+
+void Instr_call::linkReturns( Instruction * current, Instruction * returnPoint )
+{
+ while (true)
+ {
+ if ( !current || current->isUsed() )
+ return;
+
+ current->setUsed( true );
+ if ( dynamic_cast<Instr_return*>(current) || dynamic_cast<Instr_retlw*>(current) )
+ {
+// cout << "Added return link" << endl;
+// cout << " FROM: " << current->code() << endl;
+// cout << " TO: " << returnPoint->code() << endl;
+ returnPoint->addInputLink( current );
+ return;
+ }
+ if ( dynamic_cast<Instr_call*>(current) )
+ {
+ // Jump over the call instruction to its return point,
+ // which will be the instruction after current.
+ current = *(++m_pCode->find( current ));
+ continue;
+ }
+
+ const InstructionList outputs = current->outputLinks();
+
+ if ( outputs.isEmpty() )
+ return;
+
+ if ( outputs.size() == 1 )
+ current = outputs.first();
+
+ else
+ {
+ // Can't avoid function recursion now.
+ InstructionList::const_iterator end = outputs.end();
+ for ( InstructionList::const_iterator it = outputs.begin(); it != end; ++it )
+ linkReturns( *it, returnPoint );
+ return;
+ }
+ };
+}
+
+
+//TODO CLRWDT
+
+
+QString Instr_goto::code() const
+{
+ return QString("goto\t%1").arg( m_label );
+}
+
+void Instr_goto::generateLinksAndStates( Code::iterator current )
+{
+ (void)current;
+
+ makeLabelOutputLink( m_label );
+
+ m_outputState = m_inputState;
+}
+
+ProcessorBehaviour Instr_goto::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+ return behaviour;
+}
+
+
+QString Instr_iorlw::code() const
+{
+ return QString("iorlw\t%1").arg( m_literal );
+}
+
+void Instr_iorlw::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.working.value = (m_inputState.working.value | m_literal) & 0xff;
+ m_outputState.working.known |= m_literal; // Now know any bits that are one in value
+ m_outputState.status.known &= ~(1 << RegisterBit::Z);
+}
+
+ProcessorBehaviour Instr_iorlw::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+
+ behaviour.working.indep = m_literal;
+ behaviour.working.depends = ~m_literal;
+
+ behaviour.status.indep = (1 << RegisterBit::Z);;
+ return behaviour;
+}
+
+
+QString Instr_movlw::code() const
+{
+ return QString("movlw\t%1").arg( m_literal );
+}
+
+void Instr_movlw::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+ m_outputState = m_inputState;
+ m_outputState.working.known = 0xff;
+ m_outputState.working.value = m_literal;
+}
+
+ProcessorBehaviour Instr_movlw::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+ behaviour.working.indep = 0xff;
+ return behaviour;
+}
+
+
+QString Instr_retfie::code() const
+{
+ return "retfie";
+}
+
+void Instr_retfie::generateLinksAndStates( Code::iterator current )
+{
+ // Don't generate any output links
+ (void)current;
+
+ m_inputState = m_outputState;
+}
+
+ProcessorBehaviour Instr_retfie::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+ return behaviour;
+}
+
+
+QString Instr_retlw::code() const
+{
+ return QString("retlw\t%1").arg( m_literal );
+}
+
+void Instr_retlw::generateLinksAndStates( Code::iterator current )
+{
+ (void)current;
+
+ m_outputState = m_inputState;
+ m_outputState.working.known = 0xff;
+ m_outputState.working.value = m_literal;
+}
+
+ProcessorBehaviour Instr_retlw::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+ behaviour.working.indep = 0xff;
+ return behaviour;
+}
+
+
+
+QString Instr_return::code() const
+{
+ return "return";
+}
+
+void Instr_return::generateLinksAndStates( Code::iterator current )
+{
+ (void)current;
+
+ m_outputState = m_inputState;
+}
+
+ProcessorBehaviour Instr_return::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+ return behaviour;
+}
+
+
+QString Instr_sleep::code() const
+{
+ return "sleep";
+}
+
+void Instr_sleep::generateLinksAndStates( Code::iterator current )
+{
+ // Don't generate any output links
+ (void)current;
+
+ m_outputState = m_inputState;
+ m_outputState.status.value &= ~(1 << RegisterBit::NOT_PD);
+ m_outputState.status.value |= (1 << RegisterBit::NOT_TO);
+ m_outputState.status.known |= (1 << RegisterBit::NOT_TO) | (1 << RegisterBit::NOT_PD);
+}
+
+ProcessorBehaviour Instr_sleep::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+ behaviour.status.indep = (1 << RegisterBit::NOT_TO) | (1 << RegisterBit::NOT_PD);
+ return behaviour;
+}
+
+
+QString Instr_sublw::code() const
+{
+ return QString("sublw\t%1").arg( m_literal );
+}
+
+void Instr_sublw::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+
+ m_outputState = m_inputState;
+ m_outputState.working.value = (m_literal - m_inputState.working.value) & 0xff;
+ m_outputState.working.known = (m_inputState.working.known == 0xff) ? 0xff : 0x00;
+ m_outputState.status.known &= ~( (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z) );
+
+ if ( m_inputState.working.minValue() > m_literal )
+ {
+ m_outputState.status.value &= ~(1 << RegisterBit::C);
+ m_outputState.status.known |= (1 << RegisterBit::C);
+ }
+ else if ( m_inputState.working.maxValue() <= m_literal )
+ {
+ m_outputState.status.value |= (1 << RegisterBit::C);
+ m_outputState.status.known |= (1 << RegisterBit::C);
+ }
+
+ if ( m_inputState.working.known == 0xff )
+ {
+ bool isZero = (m_inputState.working.value == m_literal);
+ if ( isZero )
+ m_outputState.status.value |= (1 << RegisterBit::Z);
+ else
+ m_outputState.status.value &= ~(1 << RegisterBit::Z);
+ m_outputState.status.known |= (1 << RegisterBit::Z);
+ }
+}
+
+ProcessorBehaviour Instr_sublw::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+ behaviour.working.depends = 0xff;
+ behaviour.status.indep = (1 << RegisterBit::C) | (1 << RegisterBit::DC) | (1 << RegisterBit::Z);
+ return behaviour;
+}
+
+
+QString Instr_xorlw::code() const
+{
+ return QString("xorlw\t%1").arg( m_literal );
+}
+
+void Instr_xorlw::generateLinksAndStates( Code::iterator current )
+{
+ makeOutputLinks( current );
+ m_outputState = m_inputState;
+ m_outputState.working.value = (m_inputState.working.value ^ m_literal) & 0xff;
+ m_outputState.working.known = m_inputState.working.known;
+ m_outputState.status.known &= ~(1 << RegisterBit::Z);
+}
+
+ProcessorBehaviour Instr_xorlw::behaviour() const
+{
+ ProcessorBehaviour behaviour;
+ behaviour.working.depends = 0xff;
+ behaviour.status.indep = (1 << RegisterBit::Z);
+ return behaviour;
+}
+//END Literal and Control Operations
+
+
+
+//BEGIN Microbe (non-assembly) Operations
+QString Instr_sourceCode::code() const
+{
+ QStringList sourceLines = QStringList::split("\n",m_raw);
+ return ";" + sourceLines.join("\n;");
+}
+
+
+QString Instr_asm::code() const
+{
+ return "; asm {\n" + m_raw + "\n; }";
+}
+
+
+QString Instr_raw::code() const
+{
+ return m_raw;
+}
+//END Microbe (non-assembly) Operations
+
diff --git a/microbe/instruction.h b/microbe/instruction.h
new file mode 100644
index 0000000..2d43343
--- /dev/null
+++ b/microbe/instruction.h
@@ -0,0 +1,1273 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke <daniel.jc@gmail.com> *
+ * 2005 by David Saxton <david@bluehaze.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef INSTRUCTION_H
+#define INSTRUCTION_H
+
+#include <qmap.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qvaluelist.h>
+
+class Code;
+class CodeIterator;
+class CodeConstIterator;
+class Instruction;
+class PIC14;
+
+typedef QValueList<Instruction*> InstructionList;
+
+
+/**
+Abstraction for a Register - should be used instead of a register name. Contains
+info like whether or not the adressing of the register depends on the bank
+selection.
+
+@author David Saxton
+*/
+class Register
+{
+ public:
+ enum Type
+ {
+ TMR0,
+ OPTION_REG,
+ PCL,
+ STATUS,
+ FSR,
+ PORTA,
+ TRISA,
+ PORTB,
+ TRISB,
+ EEDATA,
+ EECON1,
+ EEADR,
+ EECON2,
+ PCLATH,
+ INTCON,
+
+ // The following three are "special"
+ WORKING, // Not a register that is addressable by an address
+ GPR, // Refers to the collection of General Purpose Registers
+ none, // used in default constructor
+ };
+
+ // These banks are used for ORing together in the banks() function
+ enum Banks
+ {
+ Bank0 = 1 << 0,
+ Bank1 = 1 << 1,
+ };
+
+ /**
+ * Creates a register of the given type, giving it the appropriate name.
+ * Note that this constructor should not be used for GPR.
+ */
+ Register( Type type = none );
+ /**
+ * Construct a Register with the given name. If the name is not
+ * recognized, then it is assumed to be a GPR register.
+ */
+ Register( const QString & name );
+ /**
+ * Construct a Register with the given name. If the name is not
+ * recognized, then it is assumed to be a GPR register.
+ */
+ Register( const char * name );
+ /**
+ * @return less-than-equality between registers; name is only compared
+ * if both registers have type GPR.
+ */
+ bool operator < ( const Register & reg ) const;
+ /**
+ * @return equality between registers; name is only compared if both
+ * registers have type GPR.
+ */
+ bool operator == ( const Register & reg ) const;
+ /**
+ * @return 0x1 and 0x2 for being addressable from banks 0 and 1
+ * respectively, OR'ed together.
+ */
+ uchar banks() const;
+ /**
+ * Convenience function.
+ * @see banks
+ */
+ bool bankDependent() const;
+ /**
+ * Returns the name of the register, or the alias for the GPR.
+ */
+ QString name() const { return m_name; }
+ /**
+ * @return the type of register.
+ */
+ Type type() const { return m_type; }
+ /**
+ * From the Optimizer's perspective, it is OK to remove, change or add
+ * any instruction so long as there are no visible external changes that
+ * go against the original intention of the microbe source (a general
+ * guiding principle). Therefore, this function returns true for PORT
+ * and TRIS registers, false for everything else.
+ */
+ bool affectsExternal() const;
+
+ protected:
+ QString m_name;
+ Type m_type;
+};
+
+
+
+class RegisterBit
+{
+ public:
+ enum STATUS_bits
+ {
+ C = 0, // Carry
+ DC = 1, // Digit carry
+ Z = 2, // Zero
+ NOT_PD = 3, // Power-down
+ NOT_TO = 4, // Time-out
+ RP0 = 5, // Bank Select
+ RP1 = 6,
+ IRP = 7,
+ };
+
+ enum INTCON_bits
+ {
+ RBIF = 0,
+ INTF = 1,
+ T0IF = 2,
+ RBIE = 3,
+ INTE = 4,
+ T0IE = 5,
+ EEIE = 6,
+ GIE = 7,
+ };
+
+ enum OPTION_bits
+ {
+ PS0 = 0,
+ PS1 = 1,
+ PS2 = 2,
+ PSA = 3,
+ T0SE = 4,
+ T0CS = 5,
+ INTEDG = 6,
+ NOT_RBPU = 7,
+ };
+
+ enum EECON1_bits
+ {
+ RD = 0,
+ WR = 1,
+ WREN = 2,
+ WRERR = 3,
+ EEIF = 4,
+ };
+ /**
+ * Constructs a bit of the given register type at the given position.
+ */
+ RegisterBit( uchar bitPos = 0, Register::Type reg = Register::none );
+ /**
+ * Construct a register bit with the given name.
+ */
+ RegisterBit( const QString & name );
+ /**
+ * Construct a register bit with the given name.
+ */
+ RegisterBit( const char * name );
+ /**
+ * @warning do not trust this value! actually, this function should be
+ * removed, or the constructors fixed so that this value can be trusted.
+ * @return the register type that the bit belongs to.
+ */
+ Register::Type registerType() const { return m_registerType; }
+ /**
+ * @return the position of the bit, e.g. "5" for RP0.
+ */
+ uchar bitPos() const { return m_bitPos; }
+ /**
+ * @return the bit, e.g. "0x20" for Z.
+ */
+ uchar bit() const { return (1 << m_bitPos); }
+ /**
+ * @return the name of the bit, e.g. "Z" for Z.
+ */
+ QString name() const { return m_name; }
+
+
+ protected:
+ /**
+ * Determines the register type and bit pos from the bit name (m_name).
+ */
+ void initFromName();
+
+ Register::Type m_registerType;
+ uchar m_bitPos:3;
+ QString m_name;
+};
+
+
+
+
+/**
+Contains information on the state of a register before an instruction is
+executed.
+
+Note that all the "uchar" values in this class should be considered as the 8
+bits of a register. So for example, if known=0x2, then only the second bit of
+the register is known, and its value is given in the second bit of value.
+
+@author David Saxton
+*/
+class RegisterState
+{
+ public:
+ RegisterState();
+
+ /**
+ * Merges the known and values together, (possibly) reducing what is
+ * known.
+ */
+ void merge( const RegisterState & state );
+ /**
+ * Sets known to unknown and value to zero.
+ */
+ void reset();
+ /**
+ * Returns the bits that are definitely zero.
+ */
+ uchar definiteZeros() const { return (~value) & known; }
+ /**
+ * Returns the bits that are definitely one.
+ */
+ uchar definiteOnes() const { return value & known; }
+ /**
+ * Returns the bits that are unknown.
+ */
+ uchar unknown() const { return ~known; }
+ /**
+ * @return the largest possible value that this register might be
+ * storing, based on which bits are known and the value of those bits.
+ */
+ uchar maxValue() const { return (value & known) | (~known); }
+ /**
+ * @return the smallest possible value that this register might be
+ * storing, based on which bits are known and the value of those bits.
+ */
+ uchar minValue() const { return (value & known); }
+ /**
+ * @return whether the known and value uchars are equal
+ */
+ bool operator == ( const RegisterState & state ) const;
+ /**
+ * @return whether either of the known and value uchars are not equal.
+ */
+ bool operator != ( const RegisterState & state ) const { return !( *this == state ); }
+ /**
+ * Prints known and value.
+ */
+ void print();
+
+ /// Whether or not the value is known (for each bit).
+ uchar known;
+
+ /// The value of the register.
+ uchar value;
+};
+
+
+/**
+Setting and dependency information for register bits. See the respective member
+descriptions for more information.
+
+@author David Saxton
+*/
+class RegisterBehaviour
+{
+ public:
+ RegisterBehaviour();
+ /**
+ * Sets "depends", "indep" and "changes" to 0x0.
+ */
+ void reset();
+
+ /**
+ * The bits whose value before the instruction is executed will affect
+ * the processor state after execution. So for example,
+ * in MOVLW this will be 0x0;
+ * in ANDLW this will be the bits that are non-zero in the literal;
+ * in BTFSC this will be the bit being tested (if this is the register
+ * being tested).
+ */
+ uchar depends;
+
+ /**
+ * The bits whose value after the instruction is executed is independent
+ * of the value before execution. So for example,
+ * in MOVLW, this will be 0xff;
+ * in ANDLW this will be the bits that are zero in the literal;
+ * in BTFSC this will be 0x0.
+ */
+ uchar indep;
+};
+
+
+
+/**
+Contains information on the state of a processor; e.g. register values
+
+@author David Saxton
+ */
+class ProcessorState
+{
+ public:
+ ProcessorState();
+ /**
+ * Calls merge for each RegisterState.
+ */
+ void merge( const ProcessorState & state );
+ /**
+ * Calls reset() for each RegisterState.
+ */
+ void reset();
+ /**
+ * @return state for the given register.
+ */
+ RegisterState & reg( const Register & reg );
+ /**
+ * @return state for the given register.
+ */
+ RegisterState reg( const Register & reg ) const;
+ /**
+ * @return whether all the RegisterStates are identical
+ */
+ bool operator == ( const ProcessorState & state ) const;
+ /**
+ * @return whether any of the RegisterStates are not equal.
+ */
+ bool operator != ( const ProcessorState & state ) const { return !( *this == state ); }
+ /**
+ * Displays each register's name and calls RegisterState::print in turn.
+ */
+ void print();
+
+ /// The working register
+ RegisterState working;
+
+ /// The status register
+ RegisterState status;
+
+ protected:
+ typedef QMap< Register, RegisterState > RegisterMap;
+ /**
+ * All registers other than working and status. Entries are created on
+ * calls to reg with a new Register.
+ */
+ RegisterMap m_registers;
+};
+
+
+/**
+Contains behavioural information for each register.
+
+@author David Saxton
+*/
+class ProcessorBehaviour
+{
+ public:
+ ProcessorBehaviour();
+ /**
+ * Calls reset() for each RegisterBehaviour.
+ */
+ void reset();
+ /**
+ * @return behaviour for the given register.
+ */
+ RegisterBehaviour & reg( const Register & reg );
+
+ /// The working register
+ RegisterBehaviour working;
+
+ /// The status register
+ RegisterBehaviour status;
+
+ protected:
+ typedef QMap< Register, RegisterBehaviour > RegisterMap;
+ /**
+ * All registers other than working and status. Entries are created on
+ * calls to reg with a new Register.
+ */
+ RegisterMap m_registers;
+};
+
+
+/**
+Contains information on whether a register is overwritten before its value is
+used. Each uchar respresents the 8 bits of the register; if the bit is 1, then
+the corresponding bit of the register is used by the Instruction or one
+of its outputs before it is overwritten.
+
+@author David Saxton
+*/
+class RegisterDepends
+{
+ public:
+ RegisterDepends();
+ /**
+ * Sets all the depends values to 0x0.
+ */
+ void reset();
+ /**
+ * @return behaviour for the given register.
+ */
+ uchar & reg( const Register & reg );
+
+ /// The working register
+ uchar working;
+
+ /// The status register
+ uchar status;
+
+ protected:
+ typedef QMap< Register, uchar > RegisterMap;
+ /**
+ * All registers other than working and status. Entries are created on
+ * calls to reg with a new Register.
+ */
+ RegisterMap m_registers;
+};
+
+
+
+/**
+Holds a program structure; an (ordered) list of blocks of code, each of which
+contains a list of instructions. The structure is such as to provide easy
+manipulation of the program, as well as aiding the optimizer.
+
+@author David Saxton
+*/
+class Code
+{
+ public:
+ Code();
+
+ typedef CodeIterator iterator;
+ typedef CodeConstIterator const_iterator;
+
+ enum InstructionPosition
+ {
+ InterruptHandler = 0,
+ LookupTable = 1,
+ Middle = 2, ///< Used for main code
+ Subroutine = 3, ///< Used for subroutines
+
+ PositionCount = 4, ///< This must remain the last item and be the number of valid positions
+ };
+
+ CodeIterator begin();
+ CodeIterator end();
+ CodeConstIterator begin() const;
+ CodeConstIterator end() const;
+
+ /**
+ * Queues a label to be given to the next instruction to be added in the
+ * given position
+ */
+ void queueLabel( const QString & label, InstructionPosition position = Middle );
+ /**
+ * Returns the list of queued labels for the given position. This is
+ * used in merging code, as we also need to merge any queued labels.
+ */
+ QStringList queuedLabels( InstructionPosition position ) const { return m_queuedLabels[position]; }
+ /**
+ * Adds the Instruction at the given position.
+ */
+ void append( Instruction * instruction, InstructionPosition position = Middle );
+ /**
+ * @returns the Instruction with the given label (or null if no such
+ * Instruction).
+ */
+ Instruction * instruction( const QString & label ) const;
+ /**
+ * Look for an Assembly instruction (other types are ignored).
+ * @return an iterator to the current instruction, or end if it wasn't
+ * found.
+ */
+ iterator find( Instruction * instruction );
+ /**
+ * Removes the Instruction (regardless of position).
+ * @warning You should always use only this function to remove an
+ * instruction as this function handles stuff such as pushing labels
+ * from this instruction onto the next before deletion.
+ */
+ void removeInstruction( Instruction * instruction );
+ /**
+ * Merges all the blocks output together with other magic such as adding
+ * variables, gpasm directives, etc.
+ */
+ QString generateCode( PIC14 * pic ) const;
+ /**
+ * Appends the InstructionLists to the end of the ones in this instance.
+ * @param middleInsertionPosition is the position where the middle code
+ * blocks of the given code will be merged at.
+ */
+ void merge( Code * code, InstructionPosition middleInsertionPosition = Middle );
+ /**
+ * @returns the InstructionList for the given insertion position.
+ */
+ InstructionList * instructionList( InstructionPosition position ) { return & m_instructionLists[position]; }
+ /**
+ * @returns the InstructionList for the given insertion position.
+ */
+ const InstructionList * instructionList( InstructionPosition position ) const { return & m_instructionLists[position]; }
+ /**
+ * Calls generateOutputLinks for each Instruction
+ */
+ void generateLinksAndStates();
+ /**
+ * Calls setUsed(false) for all instructions.
+ */
+ void setAllUnused();
+ /**
+ * Does any work that is needed to the code before it can be passed to
+ * the optimizer (such as flushing out queued labels). This is called
+ * after all the instructions have been added to the code.
+ */
+ void postCompileConstruct();
+
+ protected:
+ /**
+ * Used when generating the code. Finds the list of general purpose
+ * registers that are referenced and returns their aliases.
+ */
+ QStringList findVariables() const;
+
+ InstructionList m_instructionLists[ PositionCount ]; ///< @see InstructionPosition
+ QStringList m_queuedLabels[ PositionCount ]; ///< @see InstructionPosition
+
+ private: // Disable copy constructor and operator=
+ Code( const Code & );
+ Code &operator=( const Code & );
+};
+
+
+/**
+Iterates over all the instructions, going seamlessly between the different lists
+and avoiding the non-assembly instructions.
+
+@author David Saxton
+ */
+class CodeIterator
+{
+ public:
+ bool operator != ( const CodeIterator & i ) const { return it != i.it; }
+ bool operator == ( const CodeIterator & i ) const { return it == i.it; }
+ CodeIterator & operator ++ ();
+ Instruction * & operator * () { return *it; }
+ /**
+ * Deletes the instruction that this iterator is currently pointing at
+ * (removing it from any lists), and increments the iterator to the next
+ * instruction.
+ */
+ CodeIterator & removeAndIncrement();
+ /**
+ * Inserts the given instruction before the instruction pointed at by
+ * this iterator.
+ */
+ void insertBefore( Instruction * ins );
+
+ InstructionList::iterator it;
+ InstructionList::iterator listEnd;
+ Code::InstructionPosition pos;
+ Code * code;
+ InstructionList * list;
+};
+
+
+/**
+A const version of CodeIterator (cannot change instructions).
+
+@author David Saxton
+ */
+class CodeConstIterator
+{
+ public:
+ bool operator != ( const CodeConstIterator & i ) const { return it != i.it; }
+ bool operator == ( const CodeConstIterator & i ) const { return it == i.it; }
+ CodeConstIterator & operator ++ ();
+ const Instruction * operator * () const { return *it; }
+
+ InstructionList::const_iterator it;
+ InstructionList::const_iterator listEnd;
+ Code::InstructionPosition pos;
+ const Code * code;
+ const InstructionList * list;
+};
+
+
+/**
+@author Daniel Clarke
+@author David Saxton
+*/
+class Instruction
+{
+ public:
+ enum InstructionType
+ {
+ Assembly,
+ Raw, // User-inserted assembly
+ Comment,
+ };
+ /**
+ * Used in optimization. Note that this follows roughly, but not
+ * exactly, the Microchip classifications of similar categories.
+ */
+ enum AssemblyType
+ {
+ /**
+ * Writes to a file (which can be obtained by calling outputReg().
+ */
+ FileOriented,
+
+ /**
+ * Writes to a file bit (so BCF or BSF).
+ */
+ BitOriented,
+
+ /**
+ * Affects the working register via a literal operation, with no
+ * branching (so excludes retlw).
+ */
+ WorkingOriented,
+
+ /**
+ * Assembly instructions that don't come under the above categories
+ * (so control and branching instructions).
+ */
+ Other,
+
+ /**
+ * The Instruction is not of Assembly InstructionType.
+ */
+ None,
+ };
+
+ Instruction();
+ virtual ~Instruction();
+ void setCode( Code * code ) { m_pCode = code; }
+
+ /**
+ * This is used to decide how to output the instruction, and which
+ * instructions to avoid while optimizing.
+ */
+ virtual InstructionType type() const { return Assembly; }
+ /**
+ * @return the AssemblyType (None for non-Assembly instructions).
+ */
+ virtual AssemblyType assemblyType() const = 0;
+ /**
+ * The text to output to the generated assembly.
+ */
+ virtual QString code() const = 0;
+ /**
+ * The input processor state is used to generate the outputlinks and the
+ * output processor state.
+ */
+ void setInputState( const ProcessorState & processorState ) { m_inputState = processorState; }
+ /**
+ * By using the ProcessorState, the Instruction should:
+ * * Find all instructions that could be executed after this instruction.
+ * * Generate the output ProcessorState.
+ * The default behaviour of this function is to link to the next
+ * sequential instruction, and to generate an unknown ProcessorState.
+ * @warning if your instruction depends on any bits, then it must
+ * reinherit this function and say so.
+ * @param instruction points at this instruction
+ */
+ virtual void generateLinksAndStates( Code::iterator instruction );
+ /**
+ * @return the processor behaviour for this instruction.
+ */
+ virtual ProcessorBehaviour behaviour() const;
+ /**
+ * An input link is an instruction that might be executed immediately
+ * before this Instruction.
+ */
+ void addInputLink( Instruction * inputLink );
+ /**
+ * An output link is an instruction that might be executed immediately
+ * after this Instruction.
+ */
+ void addOutputLink( Instruction * inputLink );
+ /**
+ * The list of instructions that might be executed immediately before
+ * this instruction.
+ * @see addInputLink
+ */
+ InstructionList inputLinks() const { return m_inputLinks; }
+ /**
+ * The list of instructions that might be executed immediately after
+ * this instruction. Instruction does not generate these links; instead
+ * the list is generated Code::generateLinksAndStates function.
+ */
+ InstructionList outputLinks() const { return m_outputLinks; }
+ /**
+ * Remove the given input link from the instruction.
+ */
+ void removeInputLink( Instruction * ins );
+ /**
+ * Remove the given output link from the instruction.
+ */
+ void removeOutputLink( Instruction * ins );
+ /**
+ * Clears all input and output links from this instruction. This does
+ * not remove references to this instruction from other instructions.
+ */
+ void clearLinks();
+ /**
+ * An instruction may have zero, or more than zero labels associated
+ * with it - these will be printed before the instruction in the
+ * assembly output.
+ */
+ QStringList labels() const { return m_labels; }
+ /**
+ * @see labels
+ */
+ void addLabels( const QStringList & labels );
+ /**
+ * @see labels
+ */
+ void setLabels( const QStringList & labels );
+ /**
+ * @see used
+ */
+ void setUsed( bool used ) { m_bUsed = used; }
+ /**
+ * Used for optimization purposes in determining whether the instruction
+ * has been examined yet (to avoid infinite loops).
+ */
+ bool isUsed() const { return m_bUsed; }
+ /**
+ * Set by the optimizer to indicate whether this instruction or any of
+ * its outputs overwrite any of the bits of the given register.
+ */
+ void setRegisterDepends( uchar depends, const Register & reg ) { m_registerDepends.reg(reg) = depends; }
+ /**
+ * @see setOutputsOverwriteWorking
+ */
+ uchar registerDepends( const Register & reg ) { return m_registerDepends.reg(reg); }
+ /**
+ * Resets the overwrites.
+ */
+ void resetRegisterDepends() { m_registerDepends.reset(); }
+ /**
+ * @return the input processor state to this instruction.
+ * @see setInputState
+ */
+ ProcessorState inputState() const { return m_inputState; }
+ /**
+ * @return the output processor state from this instruction.
+ * @see generateLinksAndStates.
+ */
+ ProcessorState outputState() const { return m_outputState; }
+ /**
+ * Only applicable to Instructions that refer to a file.
+ */
+ Register file() const { return m_file; }
+ /**
+ * Only applicable to Instructions that refer to a bit (such as BCF).
+ */
+ RegisterBit bit() const { return m_bit; }
+ /**
+ * Only applicable to instructions that refer to a literal (such as
+ * XORLW).
+ */
+ uchar literal() const { return m_literal; }
+ /**
+ * Applicable only to instructions that save a result to working or file
+ * depending on the destination bit.
+ */
+ Register outputReg() const { return (m_dest == 0) ? Register::WORKING : m_file; }
+ /**
+ * Applicable only to instructions that use the destination flag.
+ */
+ unsigned dest() const { return m_dest; }
+
+ protected:
+ /**
+ * This function is provided for convenience; it creates links to the
+ * first or second instructions after this one, depending on the value
+ * of firstOutput and secondOutput.
+ * @see generateOutputLinks
+ */
+ void makeOutputLinks( Code::iterator current, bool firstOutput = true, bool secondOutput = false );
+ /**
+ * This function is provided for instructions that jump to a label (i.e.
+ * call and goto).
+ */
+ void makeLabelOutputLink( const QString & label );
+
+ RegisterDepends m_registerDepends;
+ bool m_bInputStateChanged;
+ bool m_bUsed;
+ bool m_bPositionAffectsBranching;
+ InstructionList m_inputLinks;
+ InstructionList m_outputLinks;
+ QStringList m_labels;
+ Code * m_pCode;
+
+ // Commonly needed member variables for assembly instructions
+ Register m_file;
+ RegisterBit m_bit;
+ QString m_raw; // Used by source code, raw asm, etc
+ uchar m_literal;
+ unsigned m_dest:1; // is 0 (W) or 1 (file).
+ ProcessorState m_inputState;
+ ProcessorState m_outputState;
+
+ private: // Disable copy constructor and operator=
+ Instruction( const Instruction & );
+ Instruction &operator=( const Instruction & );
+};
+
+
+
+//BEGIN Byte-Oriented File Register Operations
+class Instr_addwf : public Instruction
+{
+ public:
+ Instr_addwf( const Register & file, int dest ) { m_file = file; m_dest = dest; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+
+
+class Instr_andwf : public Instruction
+{
+ public:
+ Instr_andwf( const Register & file, int dest ) { m_file = file; m_dest = dest; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+
+
+class Instr_clrf : public Instruction
+{
+ public:
+ Instr_clrf( const Register & file ) { m_file = file; m_dest = 1; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+
+
+//TODO CLRW
+//TODO COMF
+
+
+class Instr_decf : public Instruction
+{
+ public:
+ Instr_decf( const Register & file, int dest ) { m_file = file; m_dest = dest; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+
+
+class Instr_decfsz : public Instruction
+{
+ public:
+ Instr_decfsz( const Register & file, int dest ) { m_file = file; m_dest = dest; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+
+
+class Instr_incf : public Instruction
+{
+ public:
+ Instr_incf( const Register & file, int dest ) { m_file = file; m_dest = dest; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+
+
+//TODO INCFSZ
+
+
+class Instr_iorwf : public Instruction
+{
+ public:
+ Instr_iorwf( const Register & file, int dest ) { m_file = file; m_dest = dest; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+
+
+class Instr_movf : public Instruction
+{
+ public:
+ Instr_movf( const Register & file, int dest ) { m_file = file; m_dest = dest; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+
+
+class Instr_movwf : public Instruction
+{
+ public:
+ Instr_movwf( const Register & file ) { m_file = file; m_dest = 1; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+
+
+//TODO NOP
+
+
+class Instr_rlf : public Instruction
+{
+ public:
+ Instr_rlf( const Register & file, int dest ) { m_file = file; m_dest = dest; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+
+
+class Instr_rrf : public Instruction
+{
+ public:
+ Instr_rrf( const Register & file, int dest ) { m_file = file; m_dest = dest; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+
+
+class Instr_subwf : public Instruction
+{
+ public:
+ Instr_subwf( const Register & file, int dest ) { m_file = file; m_dest = dest; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+
+
+class Instr_swapf : public Instruction
+{
+ public:
+ Instr_swapf( const Register & file, int dest ) { m_file = file; m_dest = dest; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+
+
+class Instr_xorwf : public Instruction
+{
+ public:
+ Instr_xorwf( const Register & file, int dest ) { m_file = file; m_dest = dest; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return FileOriented; }
+};
+//END Byte-Oriented File Register Operations
+
+
+
+//BEGIN Bit-Oriented File Register Operations
+class Instr_bcf : public Instruction
+{
+ public:
+ Instr_bcf( const Register & file, const RegisterBit & bit ) { m_file = file; m_bit = bit; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return BitOriented; }
+};
+
+
+class Instr_bsf : public Instruction
+{
+ public:
+ Instr_bsf( const Register & file, const RegisterBit & bit ) { m_file = file; m_bit = bit; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return BitOriented; }
+};
+
+
+class Instr_btfsc : public Instruction
+{
+ public:
+ Instr_btfsc( const Register & file, const RegisterBit & bit ) { m_file = file; m_bit = bit; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return Other; }
+};
+
+
+class Instr_btfss : public Instruction
+{
+ public:
+ Instr_btfss( const Register & file, const RegisterBit & bit ) { m_file = file; m_bit = bit; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return Other; }
+};
+//END Bit-Oriented File Register Operations
+
+
+
+//BEGIN Literal and Control Operations
+class Instr_addlw : public Instruction
+{
+ public:
+ Instr_addlw( int literal ) { m_literal = literal; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return WorkingOriented; }
+};
+
+
+
+class Instr_andlw : public Instruction
+{
+ public:
+ Instr_andlw( int literal ) { m_literal = literal; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return WorkingOriented; }
+};
+
+
+class Instr_call : public Instruction
+{
+ public:
+ Instr_call( const QString & label ) { m_label = label; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return Other; }
+ /**
+ * Called from Code after all the output links have been generated. The
+ * instruction that is called has its output links followed, and any
+ * returns encountered are linked back to the instruction after this
+ * one.
+ * @param next the instruction after this one which the return points
+ * will be linked to.
+ */
+ void makeReturnLinks( Instruction * next );
+
+ QString label() const { return m_label; }
+ void setLabel( const QString & label ) { m_label = label; }
+
+ protected:
+ /**
+ * Used by makeReturnLinks. Recursively follows the instruction's output
+ * links, until a return is found - then, link the return point back to
+ * the instruction after this one. Call instructions found while
+ * following the output are ignored.
+ * @param returnPoint the instruction to link back to on finding a
+ * return.
+ */
+ void linkReturns( Instruction * current, Instruction * returnPoint );
+
+ QString m_label;
+};
+
+
+//TODO CLRWDT
+
+
+class Instr_goto : public Instruction
+{
+ public:
+ Instr_goto( const QString & label ) { m_label = label; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return Other; }
+
+ QString label() const { return m_label; }
+ void setLabel( const QString & label ) { m_label = label; }
+
+ protected:
+ QString m_label;
+};
+
+
+class Instr_iorlw : public Instruction
+{
+ public:
+ Instr_iorlw( int literal ) { m_literal = literal; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return WorkingOriented; }
+};
+
+
+class Instr_movlw : public Instruction
+{
+ public:
+ Instr_movlw( int literal ) { m_literal = literal; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return WorkingOriented; }
+};
+
+
+class Instr_retfie : public Instruction
+{
+ public:
+ Instr_retfie() {};
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return Other; }
+};
+
+
+class Instr_retlw : public Instruction
+{
+ public:
+ Instr_retlw( int literal ) { m_literal = literal; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return Other; }
+};
+
+
+class Instr_return : public Instruction
+{
+ public:
+ Instr_return() {};
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return Other; }
+};
+
+
+class Instr_sleep : public Instruction
+{
+ public:
+ Instr_sleep() {};
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return Other; }
+};
+
+
+class Instr_sublw : public Instruction
+{
+ public:
+ Instr_sublw( int literal ) { m_literal = literal; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return WorkingOriented; }
+};
+
+
+class Instr_xorlw : public Instruction
+{
+ public:
+ Instr_xorlw( int literal ) { m_literal = literal; }
+ virtual QString code() const;
+ virtual void generateLinksAndStates( Code::iterator current );
+ virtual ProcessorBehaviour behaviour() const;
+ virtual AssemblyType assemblyType() const { return WorkingOriented; }
+};
+//END Literal and Control Operations
+
+
+
+//BEGIN Microbe (non-assembly) Operations
+class Instr_sourceCode : public Instruction
+{
+ public:
+ Instr_sourceCode( const QString & source ) { m_raw = source; }
+ virtual QString code() const;
+ virtual InstructionType type() const { return Comment; }
+ virtual AssemblyType assemblyType() const { return None; }
+};
+
+
+class Instr_asm : public Instruction
+{
+ public:
+ Instr_asm( const QString & raw ) { m_raw = raw; }
+ virtual QString code() const;
+ virtual InstructionType type() const { return Raw; }
+ virtual AssemblyType assemblyType() const { return None; }
+};
+
+
+// Like Instr_asm, but does not put ;asm {} in, used
+// for internal things like gpasm directives etc...
+class Instr_raw : public Instruction
+{
+ public:
+ Instr_raw( const QString & raw ) { m_raw = raw; }
+ virtual QString code() const;
+ virtual InstructionType type() const { return Raw; }
+ virtual AssemblyType assemblyType() const { return None; }
+};
+//END Microbe (non-assembly) Operations
+
+
+
+#endif
diff --git a/microbe/main.cpp b/microbe/main.cpp
new file mode 100644
index 0000000..1325159
--- /dev/null
+++ b/microbe/main.cpp
@@ -0,0 +1,80 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * Copyright (C) 2005 by David Saxton *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "microbe.h"
+#include "pic14.h"
+
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <qfile.h>
+
+#include <iostream>
+#include <fstream>
+using namespace std;
+
+static const char description[] =
+ I18N_NOOP("The Microbe Compiler");
+
+static const char version[] = "0.3";
+
+static KCmdLineOptions options[] =
+{
+ { "show-source", I18N_NOOP( "Show source code lines in assembly output"),0},
+ { "nooptimize", I18N_NOOP( "Do not attempt optimization of generated instructions."),0},
+ { "+[Input URL]", I18N_NOOP( "Input filename" ),0},
+ { "+[Output URL]", I18N_NOOP( "Output filename" ),0},
+ KCmdLineLastOption
+};
+
+int main(int argc, char **argv)
+{
+ KAboutData about("microbe", I18N_NOOP("Microbe"), version, description,
+ KAboutData::License_GPL, "(C) 2004-2005, The KTechlab developers", 0, "http://ktechlab.org", "ktechlab-devel@lists.sourceforge.net" );
+ about.addAuthor( "Daniel Clarke", 0, "daniel.jc@gmail.com" );
+ about.addAuthor( "David Saxton", 0, "david@bluehaze.org" );
+ KCmdLineArgs::init(argc, argv, &about);
+ KCmdLineArgs::addCmdLineOptions( options );
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ if(args->count() == 2 )
+ {
+ Microbe mb;
+ QString s = mb.compile( args->arg(0), args->isSet("show-source"), args->isSet("optimize"));
+ QString errorReport = mb.errorReport();
+
+ if ( !errorReport.isEmpty() )
+ {
+ cerr << mb.errorReport();
+ return 1; // If there was an error, don't write the output to file.
+ }
+
+ else
+ {
+ ofstream out(args->arg(1));
+ out << s;
+ return 0;
+ }
+ }
+ else args->usage();
+}
+
diff --git a/microbe/microbe.cpp b/microbe/microbe.cpp
new file mode 100644
index 0000000..d94cba7
--- /dev/null
+++ b/microbe/microbe.cpp
@@ -0,0 +1,472 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke daniel.jc@gmail.com *
+ * Copyright (C) 2005 by David Saxton *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "instruction.h"
+#include "microbe.h"
+#include "parser.h"
+#include "optimizer.h"
+#include "pic14.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <qfile.h>
+
+#include <iostream>
+using namespace std;
+
+
+//BEGIN class Microbe
+Microbe::Microbe()
+{
+ m_maxDelaySubroutine = PIC14::Delay_None;
+ m_dest = 0;
+ m_uniqueLabel = 0;
+
+ // Hardwired constants
+ m_aliasList["true"] = "1";
+ m_aliasList["false"] = "0";
+ // Things starting with b are reserved by gpasm (for binary numbers)
+ m_aliasList["b"] = "_b";
+
+ //BEGIN Keypad values
+ int bv[4][6] = {
+ { 1, 2, 3, 10, 14, 18 },
+ { 4, 5, 6, 11, 15, 19 },
+ { 7, 8, 9, 12, 16, 20 },
+ { 253, 0, 254, 13, 17, 21 } };
+
+ for ( unsigned row = 0; row < 4; ++row )
+ {
+ for ( unsigned col = 0; col < 6; ++col )
+ {
+ m_aliasList[ QString("Keypad_%1_%2").arg(row+1).arg(col+1) ] = QString::number( bv[row][col] );
+ }
+ }
+
+ m_aliasList[ "Keypad_None" ] = "0xff";
+ //END Keypad values
+}
+
+
+Microbe::~Microbe()
+{
+}
+
+
+QString Microbe::compile( const QString & url, bool showSource, bool optimize )
+{
+ QFile file( url );
+ if( file.open( IO_ReadOnly ) )
+ {
+ QTextStream stream(&file);
+ unsigned line = 0;
+ while( !stream.atEnd() )
+ m_program += SourceLine( stream.readLine(), url, line++ );
+ file.close();
+ simplifyProgram();
+ }
+ else
+ {
+ m_errorReport += i18n("Could not open file '%1'\n").arg(url);
+ return 0;
+ }
+
+ Parser parser(this);
+
+ // Extract the PIC ID
+ if ( !m_program.isEmpty() )
+ {
+ m_picType = PIC14::toType( m_program[0].text() );
+ m_program.remove( m_program.begin() );
+ }
+
+ PIC14 * pic = makePic();
+ if ( !pic )
+ return 0;
+
+ Code * code = parser.parse( m_program );
+ pic->setCode( code );
+ pic->addCommonFunctions( (PIC14::DelaySubroutine)m_maxDelaySubroutine );
+
+ pic->postCompileConstruct( m_usedInterrupts );
+ code->postCompileConstruct();
+
+ if ( optimize )
+ {
+ Optimizer opt;
+ opt.optimize( code );
+ }
+
+ return code->generateCode( pic );
+}
+
+
+PIC14 * Microbe::makePic()
+{
+ return new PIC14( this, (PIC14::Type)m_picType );
+}
+
+
+void Microbe::simplifyProgram()
+{
+ SourceLineList simplified;
+
+ enum CommentType { None, SingleLine, MultiLine };
+ CommentType commentType = None;
+
+ SourceLineList::const_iterator end = m_program.end();
+ for ( SourceLineList::const_iterator it = m_program.begin(); it != end; ++it )
+ {
+ QString code = (*it).text();
+ QString simplifiedLine;
+
+ if ( commentType == SingleLine )
+ commentType = None;
+
+ unsigned l = code.length();
+
+ for ( unsigned i = 0; i < l; ++i )
+ {
+ QChar c = code[i];
+ switch ( c )
+ {
+ case '/':
+ // Look for start of comments in form "//" and "/*"
+
+ if ( commentType == None && (i+1 < l) )
+ {
+ if ( code[i+1] == '/' )
+ {
+ commentType = SingleLine;
+ i++;
+ }
+
+ else if ( code[i+1] == '*' )
+ {
+ commentType = MultiLine;
+ i++;
+ }
+ }
+ break;
+
+ case '*':
+ // Look for end of comments in form "*/"
+ if ( commentType == MultiLine && (i+1 < l) && code[i+1] == '/' )
+ {
+ i++;
+ commentType = None;
+ continue;
+ }
+ break;
+
+ case '{':
+ case '}':
+ // Put braces on seperate lines
+
+ if ( commentType != None )
+ break;
+
+ simplified << SourceLine( simplifiedLine.simplifyWhiteSpace(), (*it).url(), (*it).line() );
+ simplified << SourceLine( c, (*it).url(), (*it).line() );
+
+ simplifiedLine = "";
+ continue;
+ }
+
+ if ( commentType == None )
+ simplifiedLine += c;
+ }
+
+ simplified << SourceLine( simplifiedLine.simplifyWhiteSpace(), (*it).url(), (*it).line() );
+ }
+
+ m_program.clear();
+ end = simplified.end();
+ for ( SourceLineList::const_iterator it = simplified.begin(); it != end; ++it )
+ {
+ if ( !(*it).text().isEmpty() )
+ m_program << *it;
+ }
+}
+
+
+void Microbe::compileError( MistakeType type, const QString & context, const SourceLine & sourceLine )
+{
+ QString message;
+ switch (type)
+ {
+ case UnknownStatement:
+ message = i18n("Unknown statement");
+ break;
+ case InvalidPort:
+ message = i18n("Port '%1' is not supported by target PIC").arg(context);
+ break;
+ case UnassignedPin:
+ message = i18n("Pin identifier was not followed by '='");
+ break;
+ case NonHighLowPinState:
+ message = i18n("Pin state can only be 'high' or 'low'");
+ break;
+ case UnassignedPort:
+ message = i18n("Invalid token '%1'. Port identifier should be followed by '='").arg(context);
+ break;
+ case UnexpectedStatementBeforeBracket:
+ message = i18n("Unexpected statement before '{'");
+ break;
+ case MismatchedBrackets:
+ message = i18n("Mismatched brackets in expression '%1'").arg(context);
+ break;
+ case InvalidEquals:
+ message = i18n("Invalid '=' found in expression");
+ break;
+ case ReservedKeyword:
+ message = i18n("Reserved keyword '%1' cannot be a variable name.").arg(context);
+ break;
+ case ConsecutiveOperators:
+ message = i18n("Nothing between operators");
+ break;
+ case MissingOperator:
+ message = i18n("Missing operator or space in operand");
+ break;
+ case UnknownVariable:
+ if ( context.isEmpty() )
+ message = i18n("Unknown variable");
+ else
+ message = i18n("Unknown variable '%1'").arg(context);
+ break;
+ case UnopenableInclude:
+ message = i18n("Could not open include file '%1'").arg(context);
+ break;
+ case DivisionByZero:
+ message = i18n("Division by zero");
+ break;
+ case NumberTooBig:
+ message = i18n("Number too big");
+ break;
+ case NonConstantStep:
+ message = i18n("Step can only be a constant expression");
+ break;
+ case NonConstantDelay:
+ message = i18n("Delay must be a positive constant value");
+ break;
+ case HighLowExpected:
+ message = i18n("'high' or 'low' expected after pin expression '%1'").arg(context);
+ break;
+ case InvalidComparison:
+ message = i18n("Comparison operator in '%1' is not recognized");
+ break;
+ case SubBeforeEnd:
+ message = i18n("Subroutine definition before end of program");
+ break;
+ case InterruptBeforeEnd:
+ message = i18n("Interrupt routine definition before end of program");
+ break;
+ case LabelExpected:
+ message = i18n("Label expected");
+ break;
+ case TooManyTokens:
+ message = i18n("Extra tokens at end of line");
+ break;
+ case FixedStringExpected:
+ message = i18n("Expected '%1'").arg(context);
+ break;
+ case PinListExpected:
+ message = i18n("Pin list expected");
+ break;
+ case AliasRedefined:
+ message = i18n("Alias already definied");
+ break;
+ case InvalidInterrupt:
+ message = i18n("Interrupt type not supported by target PIC");
+ break;
+ case InterruptRedefined:
+ message = i18n("Interrupt already definied");
+ break;
+ case ReadOnlyVariable:
+ message = i18n("Variable '%1' is read only").arg(context);
+ break;
+ case WriteOnlyVariable:
+ message = i18n("Variable '%1' is write only").arg(context);
+ break;
+ case InvalidPinMapSize:
+ message = i18n("Invalid pin list size");
+ break;
+ case VariableRedefined:
+ message = i18n("Variable '%1' is already defined").arg(context);
+ break;
+ case InvalidVariableName:
+ message = i18n("'%1' is not a valid variable name").arg(context);
+ break;
+ case VariableExpected:
+ message = i18n("Variable expected");
+ break;
+ case NameExpected:
+ message = i18n("Name expected");
+ break;
+ }
+
+
+ m_errorReport += QString("%1:%2:Error [%3] %4\n")
+ .arg( sourceLine.url() )
+ .arg( sourceLine.line()+1 )
+ .arg( type )
+ .arg( message );
+}
+
+
+bool Microbe::isValidVariableName( const QString & variableName )
+{
+ if ( variableName.isEmpty() )
+ return false;
+
+ if ( !variableName[0].isLetter() && variableName[0] != '_' )
+ return false;
+
+ for ( unsigned i = 1; i < variableName.length(); ++i )
+ {
+ if ( !variableName[i].isLetterOrNumber() && variableName[i] != '_' )
+ return false;
+ }
+
+ return true;
+}
+
+
+void Microbe::addVariable( const Variable & variable )
+{
+ if ( variable.type() == Variable::invalidType )
+ return;
+
+ if ( !isVariableKnown( variable.name() ) )
+ m_variables << variable;
+}
+
+
+Variable Microbe::variable( const QString & name ) const
+{
+ VariableList::const_iterator end = m_variables.end();
+ for ( VariableList::const_iterator it = m_variables.begin(); it != end; ++it )
+ {
+ if ( (*it).name() == name )
+ return *it;
+ }
+ return Variable();
+}
+
+
+bool Microbe::isVariableKnown( const QString & name ) const
+{
+ return variable(name).type() != Variable::invalidType;
+}
+
+
+void Microbe::addDelayRoutineWanted( unsigned routine )
+{
+ if ( m_maxDelaySubroutine < routine )
+ m_maxDelaySubroutine = routine;
+}
+
+
+void Microbe::addAlias( const QString & name, const QString & dest )
+{
+ m_aliasList[name] = dest;
+}
+
+
+QString Microbe::alias( const QString & alias ) const
+{
+ // If the string is an alias, return the real string,
+ // otherwise just return the alias as that is the real string.
+ AliasMap::const_iterator it = m_aliasList.find(alias);
+ if ( it != m_aliasList.constEnd() )
+ return it.data();
+ return alias;
+}
+
+
+void Microbe::setInterruptUsed(const QString &interruptName)
+{
+ // Don't add it again if it is already in the list
+ if ( m_usedInterrupts.contains( interruptName ) )
+ return;
+ m_usedInterrupts.append(interruptName);
+}
+
+
+bool Microbe::isInterruptUsed( const QString & interruptName )
+{
+ return m_usedInterrupts.contains( interruptName );
+}
+
+
+QString Microbe::dest() const
+{
+ return QString("__op%1").arg(m_dest);
+}
+
+
+void Microbe::incDest()
+{
+ m_dest++;
+// if ( ++m_dest > m_highestDest )
+// m_highestDest = m_dest;
+}
+
+
+void Microbe::decDest()
+{
+ m_dest--;
+}
+
+
+void Microbe::resetDest()
+{
+ m_dest = 0;
+}
+//END class Microbe
+
+
+
+//BEGIN class SourceLine
+SourceLine::SourceLine( const QString & text, const QString & url, int line )
+{
+ m_text = text;
+ m_url = url;
+ m_line = line;
+}
+
+
+SourceLine::SourceLine()
+{
+ m_line = -1;
+}
+
+
+QStringList SourceLine::toStringList( const SourceLineList & lines )
+{
+ QStringList joined;
+ SourceLineList::const_iterator end = lines.end();
+ for ( SourceLineList::const_iterator it = lines.begin(); it != end; ++it )
+ joined << (*it).text();
+ return joined;
+
+}
+//END class SourceLine
+
diff --git a/microbe/microbe.h b/microbe/microbe.h
new file mode 100644
index 0000000..efa7aa4
--- /dev/null
+++ b/microbe/microbe.h
@@ -0,0 +1,249 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef MICROBE_H
+#define MICROBE_H
+
+#include <instruction.h>
+#include <variable.h>
+// #include <pic14.h>
+
+#include <qmap.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+class QString;
+class BTreeBase;
+class BTreeNode;
+class Code;
+class PIC14;
+class PortPin;
+
+typedef QValueList<PortPin> PortPinList;
+
+typedef QValueList<Variable> VariableList;
+typedef QMap<QString,QString> AliasMap;
+
+enum ExprType
+{
+ unset = 1,
+ working = 2,
+ number = 3,
+ variable = 4,
+ extpin = 5,
+ keypad = 6,
+};
+
+
+class SourceLine;
+typedef QValueList<SourceLine> SourceLineList;
+/**
+Represents a source line, with the convention of line number starting at zero.
+@author David Saxton
+*/
+class SourceLine
+{
+ public:
+ /**
+ * The QValueList template requires a default constructor - calling this
+ * though creates an invalid SourceLine with line() returning -1. So
+ * this constructor should never be used.
+ */
+ SourceLine();
+ SourceLine( const QString & text, const QString & url, int line );
+
+ QString text() const { return m_text; }
+ QString url() const { return m_url; }
+ int line() const { return m_line; }
+
+ /**
+ * Extracts the text from each SourceLine and adds it to the
+ * returned QStringList.
+ */
+ static QStringList toStringList( const SourceLineList & lines );
+
+ protected:
+ QString m_text;
+ QString m_url;
+ int m_line;
+};
+
+
+
+/**
+@author Daniel Clarke
+@author David Saxton
+*/
+class Microbe
+{
+ public:
+ Microbe();
+ ~Microbe();
+
+ enum MistakeType
+ {
+ UnknownStatement = 1,
+ InvalidPort = 2,
+ UnassignedPin = 3, // pin identifier without an "= something"
+ NonHighLowPinState = 4,
+ UnassignedPort = 5, // port identifier without an "= something"
+ UnexpectedStatementBeforeBracket = 6,
+ MismatchedBrackets = 7,
+ InvalidEquals = 8,
+ ReservedKeyword = 9,
+ ConsecutiveOperators = 10,
+ MissingOperator = 11,
+ UnknownVariable = 12,
+ UnopenableInclude = 16,
+ DivisionByZero = 17,
+ NumberTooBig = 18,
+ NonConstantStep = 19,
+ NonConstantDelay = 20,
+ HighLowExpected = 21,
+ InvalidComparison = 22,
+ SubBeforeEnd = 23,
+ LabelExpected = 24,
+ TooManyTokens = 25,
+ FixedStringExpected = 26,
+ PinListExpected = 27,
+ AliasRedefined = 28,
+ InvalidInterrupt = 29,
+ InterruptRedefined = 30,
+ InterruptBeforeEnd = 31,
+ ReadOnlyVariable = 32,
+ WriteOnlyVariable = 33,
+ InvalidPinMapSize = 34,
+ VariableRedefined = 35,
+ InvalidVariableName = 36,
+ VariableExpected = 40,
+ NameExpected = 41
+ };
+
+ /**
+ * Returns a list of errors occured during compilation, intended for
+ * outputting to stderr.
+ */
+ QString errorReport() const { return m_errorReport; }
+ /**
+ * Call this to compile the given code. This serves as the top level of
+ * recursion as it performs initialisation of things, to recurse at
+ * levels use parseUsingChild(), or create your own Parser.
+ * @param url is used for reporting errors
+ */
+ QString compile( const QString & url, bool showSource, bool optimize );
+ /**
+ * Adds the given compiler error at the file line number to the
+ * compilation report.
+ */
+ void compileError( MistakeType type, const QString & context, const SourceLine & sourceLine );
+ /**
+ * This is for generating unique numbers for computer generated labels.
+ */
+ QString uniqueLabel() { return QString("__%1").arg(m_uniqueLabel++); }
+ /**
+ * If alias is an alias for something then it returns that something,
+ * otherwise it just returns alias (which in that case is not an alias!)
+ */
+ QString alias( const QString & alias ) const;
+ /**
+ * Aliases the name to the dest.
+ */
+ void addAlias( const QString & name, const QString & dest );
+ /**
+ * Tell Microbe that a minimum of the given delay routine needs to be
+ * created.
+ * @see PIC14::DelaySubroutine
+ * @param routine - DelaySubroutine enum, higher is more priority
+ */
+ void addDelayRoutineWanted( unsigned routine );
+ /**
+ * Makes a new PIC assembly object, based on the PIC string that the
+ * user has given in the source.
+ */
+ PIC14 * makePic();
+ /**
+ * Add the interrupt as being used, i.e. make sure there is one and only
+ * one occurance of its name in m_usedInterrupts.
+ */
+ void setInterruptUsed( const QString & interruptName );
+ /**
+ * @returns whether the given interrupt has already been used.
+ */
+ bool isInterruptUsed( const QString & interruptName );
+ /**
+ * @returns whether the variable name is valid.
+ */
+ static bool isValidVariableName( const QString & variableName );
+ /**
+ * Appends the given variable name to the variable list.
+ */
+ void addVariable( const Variable & variable );
+ /**
+ * @returns the variable with the given name, or one of invalidType if
+ * no such variable exists.
+ */
+ Variable variable( const QString & variableName ) const;
+ /**
+ * @returns whether the variable has been declared yet.
+ */
+ bool isVariableKnown( const QString & variableName ) const;
+ /**
+ * This is used as a temporary variable while evaluating an expression.
+ */
+ QString dest() const;
+ void incDest();
+ void decDest();
+ void resetDest();
+
+ protected:
+ /**
+ * Strips comments from m_program, simplifies the white space in each line,
+ * puts braces on separate lines, and then removes any blank lines.
+ */
+ void simplifyProgram();
+
+ QStringList m_usedInterrupts;
+ SourceLineList m_program;
+ QString m_errorReport;
+ int m_uniqueLabel;
+ VariableList m_variables;
+ int m_dest;
+ unsigned m_maxDelaySubroutine;
+
+ /**
+ * Keeps a list of aliases that have been created which maps the key as
+ * the alias text to the data which is the thing being aliased, so that
+ * something can be aliased to two different things. e.g.
+ * alias ken bob
+ * alias mary bob
+ */
+ QMap<QString,QString> m_aliasList;
+ /**
+ * Once the child parser has found it, this is set to the pic type
+ * string found in the source file. The pic type directive must be
+ * the first thing in the microbe program, before even includes.
+ * @see PIC14::Type
+ */
+ int m_picType;
+};
+
+
+#endif
+
diff --git a/microbe/optimizer.cpp b/microbe/optimizer.cpp
new file mode 100644
index 0000000..33b0bcd
--- /dev/null
+++ b/microbe/optimizer.cpp
@@ -0,0 +1,512 @@
+/***************************************************************************
+ * Copyright (C) 2005 by David Saxton *
+ * david@bluehaze.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+
+#include "instruction.h"
+#include "optimizer.h"
+
+#include <kdebug.h>
+
+#include <assert.h>
+#include <iostream>
+using namespace std;
+
+
+QString binary( uchar val )
+{
+ QString bin = QString::number( val, 2 );
+ QString pad;
+ pad.fill( '0', 8-bin.length() );
+ return pad + bin;
+}
+
+
+Optimizer::Optimizer()
+{
+ m_pCode = 0l;
+}
+
+
+Optimizer::~Optimizer()
+{
+}
+
+
+void Optimizer::optimize( Code * code )
+{
+// return;
+ m_pCode = code;
+
+ bool changed;
+ do
+ {
+ changed = false;
+
+ // Repeatedly generate links and states until
+ // we know as much as possible about the system.
+ propagateLinksAndStates();
+
+ // Remove instructions without input links
+ changed |= pruneInstructions();
+
+ // Perform optimizations based on processor states
+ changed |= optimizeInstructions();
+ }
+ while ( changed );
+}
+
+
+void Optimizer::propagateLinksAndStates()
+{
+ int count = 0;
+
+ do
+ {
+ count++;
+ m_pCode->generateLinksAndStates();
+ }
+ while ( giveInputStates() );
+
+// cout << "count="<<count<<endl;
+}
+
+
+bool Optimizer::giveInputStates()
+{
+ bool changed = false;
+
+ Code::iterator end = m_pCode->end();
+ for ( Code::iterator it = m_pCode->begin(); it != end; ++it )
+ {
+ // Now, build up the most specific known processor state from the instructins
+ // that could be executed immediately before this instruction.
+ // This is done by taking the output state of the first input link, and
+ // then reducing it to the greatest common denominator of all the input states.
+
+ const InstructionList list = (*it)->inputLinks();
+ if ( list.isEmpty() )
+ continue;
+
+ InstructionList::const_iterator inputIt = list.begin();
+ InstructionList::const_iterator inputsEnd = list.end();
+
+ ProcessorState input = (*(inputIt++))->outputState();
+
+ while ( inputIt != inputsEnd )
+ input.merge( (*inputIt++)->outputState() );
+
+ if ( !changed )
+ {
+ ProcessorState before = (*it)->inputState();
+ bool stateChanged = ( before != input );
+ changed |= stateChanged;
+ }
+
+ (*it)->setInputState( input );
+ }
+ return changed;
+}
+
+
+bool Optimizer::pruneInstructions()
+{
+ bool removed = false;
+
+ //BEGIN remove instructions without any input links
+ Code::iterator it = m_pCode->begin();
+ Code::iterator end = m_pCode->end();
+
+ // Jump past the first instruction, as nothing (necessarily) points to that
+ if ( it != end )
+ ++it;
+
+ while ( it != end )
+ {
+ if ( (*it)->inputLinks().isEmpty() )
+ {
+// cout << "Removing: " << (*it)->code() << endl;
+ it.removeAndIncrement();
+ removed = true;
+ }
+ else
+ ++it;
+ }
+ end = m_pCode->end(); // Reset end as instructions may have been removed
+ //END remove instructions without any input links
+
+
+ //BEGIN remove labels without any reference to them
+ // First: build up a list of labels which are referenced
+ QStringList referencedLabels;
+ for ( it = m_pCode->begin(); it != end; ++it )
+ {
+ if ( Instr_goto * ins = dynamic_cast<Instr_goto*>(*it) )
+ referencedLabels << ins->label();
+ else if ( Instr_call * ins = dynamic_cast<Instr_call*>(*it) )
+ referencedLabels << ins->label();
+ }
+
+ // Now remove labels from instructions that aren't in the referencedLabels list
+ for ( it = m_pCode->begin(); it != end; ++it )
+ {
+ QStringList labels = (*it)->labels();
+
+ QStringList::iterator labelsEnd = labels.end();
+ for ( QStringList::iterator labelsIt = labels.begin(); labelsIt != labelsEnd; )
+ {
+ if ( !referencedLabels.contains( *labelsIt ) )
+ {
+ labelsIt = labels.erase( labelsIt );
+ removed = true;
+ }
+ else
+ ++labelsIt;
+ }
+
+ (*it)->setLabels( labels);
+ }
+ //END remove labels without any reference to them
+
+ return removed;
+}
+
+
+bool Optimizer::optimizeInstructions()
+{
+ //BEGIN Optimization 1: Concatenate chained GOTOs
+ // We go through the instructions looking for GOTO statements. If we find any, then
+ // we trace back through their input links to any other GOTO statements - any that
+ // are found are then redirected to point to the label that the original GOTO statement
+ // was pointing at.
+ Code::iterator end = m_pCode->end();
+ for ( Code::iterator it = m_pCode->begin(); it != end; ++it )
+ {
+ Instr_goto * gotoIns = dynamic_cast<Instr_goto*>(*it);
+ if ( !gotoIns )
+ continue;
+
+ if ( redirectGotos( gotoIns, gotoIns->label() ) )
+ return true;
+ m_pCode->setAllUnused();
+ }
+ //END Optimization 1: Concatenate chained GOTOs
+
+
+ //BEGIN Optimization 2: Remove GOTOs when jumping to the subsequent instruction
+ // Any GOTO instructions that just jump to the next instruction can be removed.
+ for ( Code::iterator it = m_pCode->begin(); it != end; ++it )
+ {
+ Instruction * next = *(++Code::iterator(it));
+ Instruction * gotoIns = dynamic_cast<Instr_goto*>(*it);
+ if ( !gotoIns || !next || (gotoIns->outputLinks().first() != next) )
+ continue;
+
+// cout << "Removing: " << gotoIns->code() << endl;
+ it.removeAndIncrement();
+ return true;
+ }
+ end = m_pCode->end();
+ //END Optimization 2: Remove GOTOs when jumping to the subsequent instruction
+
+
+ //BEGIN Optimization 3: Replace MOVWF with CLRF with W is 0
+ // We look for MOVWF instructions where the working register holds zero.
+ // We then replace the MOVWf instruction with a CLRF instruction.
+ for ( Code::iterator it = m_pCode->begin(); it != end; ++it )
+ {
+ Instr_movwf * ins = dynamic_cast<Instr_movwf*>(*it);
+ if ( !ins )
+ continue;
+
+ ProcessorState inputState = ins->inputState();
+ RegisterState working = inputState.working;
+ if ( (working.value != 0x0) || (working.known != 0xff) )
+ continue;
+
+ // CLRF sets the Z flag of STATUS to 1, but MOVWF does not set any flags.
+ // So we need to check for dependence of the Z flag if we are possibly
+ // changing the flag by replacing the instruction.
+ if ( !(inputState.status.definiteOnes() & (1 << RegisterBit::Z)) )
+ {
+ // Input state of Z flag is either unknown or low.
+
+ uchar depends = generateRegisterDepends( *it, Register::STATUS );
+ if ( depends & (1 << RegisterBit::Z) )
+ {
+ // Looks like there's some instruction that depends on the zero bit,
+ // and we about potentially about to change it.
+ continue;
+ }
+ }
+
+
+ Instr_clrf * instr_clrf = new Instr_clrf( ins->file() );
+// cout << "Replacing \""<<(*it)->code()<<"\" with \""<<instr_clrf->code()<<"\"\n";
+ it.insertBefore( instr_clrf );
+ it.removeAndIncrement();
+ return true;
+ }
+ //END Optimization 3: Replace MOVWF with CLRF with W is 0
+
+
+ //BEGIN Optimization 4: Replace writes to W with MOVLW when value is known
+ // We look for instructions with AssemblyType either WorkingOriented, or FileOriented
+ // and writing to W. Then, if the value is known and there are no instructions that
+ // depend on the STATUS bits set by the instruction, then we replace it with a MOVLW
+ for ( Code::iterator it = m_pCode->begin(); it != end; ++it )
+ {
+ if ( dynamic_cast<Instr_movlw*>(*it) )
+ {
+ // If we don't catch this condition, we'll end up in an infinite loop,
+ // repeatedly replacing the first MOVLW that we come across.
+ continue;
+ }
+
+ bool workingOriented = (*it)->assemblyType() == Instruction::WorkingOriented;
+ bool fileOriented = (*it)->assemblyType() == Instruction::FileOriented;
+ if ( !workingOriented && (!fileOriented || ((*it)->dest() != 0)) )
+ continue;
+
+ // So can now assume that workingOriented and fileOriented are logical opposites
+
+ RegisterState outputState = (*it)->outputState().working;
+ if ( outputState.known != 0xff )
+ continue;
+
+ ProcessorBehaviour behaviour = (*it)->behaviour();
+
+ // MOVLW does not set any STATUS flags, but the instruction that we are replacing
+ // might. So we must check if any of these STATUS flags are depended upon, and if so
+ // only allow replacement if the STATUS flags are not being changed.
+ if ( !canRemove( *it, Register::STATUS, behaviour.reg( Register::STATUS ).indep ) )
+ continue;
+
+ Instr_movlw * movlw = new Instr_movlw( outputState.value );
+// cout << "Replacing \""<<(*it)->code()<<"\" with \""<<movlw->code()<<"\"\n";
+ it.insertBefore( movlw );
+ it.removeAndIncrement();
+ return true;
+ }
+ //END Optimization 4: Replace writes to W with MOVLW when value is known
+
+
+ //BEGIN Optimization 5: Remove writes to a bit when the value is ignored and overwritten again
+ // We go through the instructions looking for statements that write to a bit (bcf, bsf).
+ // If we find any, then we trace through their output links to see if their value is
+ // overwritten before it is used - and if so, the instruction can be removed.
+ for ( Code::iterator it = m_pCode->begin(); it != end; ++it )
+ {
+ if ( (*it)->assemblyType() != Instruction::BitOriented )
+ continue;
+
+ const Register regSet = (*it)->file();
+
+ if ( regSet.affectsExternal() )
+ continue;
+
+ uchar bitPos = (*it)->bit().bitPos();
+
+ ProcessorState inputState = (*it)->inputState();
+ ProcessorState outputState = (*it)->outputState();
+ ProcessorBehaviour behaviour = (*it)->behaviour();
+
+ // Are we rewriting over a bit that already has the same value?
+ // (Note this check is just for the bit changing instructions, as there is a similar
+ // check for register changing actions later on when we know which bits care about
+ // being overwritten).
+ if ( inputState.reg( regSet ).known & (1 << bitPos) )
+ {
+ bool beforeVal = (inputState.reg( regSet ).value & (1 << bitPos));
+ bool afterVal = (outputState.reg( regSet ).value & (1 << bitPos));
+ if ( beforeVal == afterVal )
+ {
+// cout << "Removing: " << (*it)->code() << endl;
+ it.removeAndIncrement();
+ return true;
+ }
+ }
+
+ uchar depends = generateRegisterDepends( *it, regSet );
+ if ( !(depends & (1 << bitPos)) )
+ {
+ // Bit is overwritten before being used - so lets remove this instruction :)
+// cout << "Removing: " << (*it)->code() << endl;
+ it.removeAndIncrement();
+ return true;
+ }
+ }
+ m_pCode->setAllUnused();
+ //END Optimization 5: Remove writes to a bit when the value is ignored and overwritten again
+
+
+ //BEGIN Optimization 6: Remove writes to a register when the value is ignored and overwritten again
+ // We go through the instructions looking for statements that write to a register (such as MOVLW).
+ // If we find any, then we trace through their output links to see if their value is
+ // overwritten before it is used - and if so, the instruction can be removed.
+ for ( Code::iterator it = m_pCode->begin(); it != end; ++it )
+ {
+ bool noFile = false;
+
+ switch ( (*it)->assemblyType() )
+ {
+ case Instruction::WorkingOriented:
+ noFile = true;
+ // (no break)
+
+ case Instruction::FileOriented:
+ break;
+
+ case Instruction::BitOriented:
+ case Instruction::Other:
+ case Instruction::None:
+ continue;
+ }
+
+ const Register regSet = noFile ? Register( Register::WORKING ) : (*it)->outputReg();
+
+ if ( regSet.affectsExternal() )
+ continue;
+
+ ProcessorState inputState = (*it)->inputState();
+ ProcessorState outputState = (*it)->outputState();
+ ProcessorBehaviour behaviour = (*it)->behaviour();
+
+ // All ins_file instructions will affect at most two registers; the
+ // register it is writing to (regSet) and the status register.
+ // In i==0, test regSet
+ // In i==1, test STATUS
+ bool ok = true;
+ for ( unsigned i = 0; i < 2; ++ i)
+ {
+ // If we are testing STATUS, then we assume that the bits changed
+ // are only those that are marked as independent.
+ uchar bitmask = ( i == 1 ) ? behaviour.reg( Register::STATUS ).indep : 0xff;
+ if ( !canRemove( *it, (i == 0) ? regSet : Register::STATUS, bitmask ) )
+ {
+ ok = false;
+ break;
+ }
+ }
+
+ if ( !ok )
+ continue;
+
+ // Looks like we're free to remove the instruction :);
+// cout << "Removing: " << (*it)->code() << endl;
+ it.removeAndIncrement();
+ return true;
+ }
+ m_pCode->setAllUnused();
+ //END Optimization 6: Remove writes to a register when the value is ignored and overwritten again
+
+ return false;
+}
+
+
+bool Optimizer::redirectGotos( Instruction * current, const QString & label )
+{
+ if ( current->isUsed() )
+ return false;
+
+ current->setUsed( true );
+
+ bool changed = false;
+
+ const InstructionList list = current->inputLinks();
+ InstructionList::const_iterator end = list.end();
+ for ( InstructionList::const_iterator it = list.begin(); it != end; ++it )
+ {
+ Instr_goto * gotoIns = dynamic_cast<Instr_goto*>(*it);
+ if ( !gotoIns || (gotoIns->label() == label) )
+ continue;
+
+// cout << "Redirecting goto to label \"" << label << "\" : " << gotoIns->code() << endl;
+ gotoIns->setLabel( label );
+ changed = true;
+ }
+
+ return changed;
+}
+
+
+uchar Optimizer::generateRegisterDepends( Instruction * current, const Register & reg )
+{
+ m_pCode->setAllUnused();
+
+ const InstructionList list = current->outputLinks();
+ InstructionList::const_iterator listEnd = list.end();
+
+ uchar depends = 0x0;
+
+ for ( InstructionList::const_iterator listIt = list.begin(); listIt != listEnd; ++listIt )
+ depends |= registerDepends( *listIt, reg );
+
+ return depends;
+}
+
+
+uchar Optimizer::registerDepends( Instruction * current, const Register & reg )
+{
+ if ( current->isUsed() )
+ return current->registerDepends( reg );
+
+ current->setUsed( true );
+
+ uchar depends = 0x0;
+
+ const InstructionList list = current->outputLinks();
+ InstructionList::const_iterator end = list.end();
+ for ( InstructionList::const_iterator it = list.begin(); it != end; ++it )
+ depends |= registerDepends( *it, reg );
+
+ RegisterBehaviour behaviour = current->behaviour().reg( reg );
+ depends &= ~(behaviour.indep); // Get rid of depend bits that are set in this instruction
+ depends |= behaviour.depends; // And add the ones that are dependent in this instruction
+
+ current->setRegisterDepends( depends, reg );
+ return depends;
+}
+
+
+bool Optimizer::canRemove( Instruction * ins, const Register & reg, uchar bitMask )
+{
+ // The bits that are depended upon in the future for this register
+ uchar depends = generateRegisterDepends( ins, reg );
+
+ // Only interested in those bits allowed by the bit mask
+ depends &= bitMask;
+
+ RegisterState inputState = ins->inputState().reg( reg );
+ RegisterState outputState = ins->outputState().reg( reg );
+
+ if ( inputState.unknown() & depends )
+ {
+ // There's at least one bit whose value is depended on, but is not known before this
+ // instruction is executed. Therefore, it is not safe to remove this instruction.
+ return false;
+ }
+
+ if ( outputState.unknown() & depends )
+ {
+ // There's at least one bit whose value is depended on, but is not known after this
+ // instruction is executed. Therefore, it is not safe to remove this instruction.
+ return false;
+ }
+
+ uchar dependsInput = inputState.value & depends;
+ uchar dependsOutput = outputState.value & depends;
+ if ( dependsInput != dependsOutput )
+ {
+ // At least one bit whose value is depended upon was changed.
+ return false;
+ }
+
+ return true;
+}
+
diff --git a/microbe/optimizer.h b/microbe/optimizer.h
new file mode 100644
index 0000000..249abd0
--- /dev/null
+++ b/microbe/optimizer.h
@@ -0,0 +1,87 @@
+/***************************************************************************
+ * Copyright (C) 2005 by David Saxton *
+ * david@bluehaze.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+
+#ifndef OPTIMIZER_H
+#define OPTIMIZER_H
+
+#include "instruction.h"
+
+
+/// Used for debugging; returns the uchar as a binary string (e.g. 01101010).
+QString binary( uchar val );
+
+
+/**
+@author David Saxton
+*/
+class Optimizer
+{
+ public:
+ Optimizer();
+ ~Optimizer();
+
+ void optimize( Code * code );
+
+ protected:
+ /**
+ * Repeatedly generates links and states for the instructions and
+ * refining their input states, until equilibrium in the input states
+ * is reached.
+ */
+ void propagateLinksAndStates();
+ /**
+ * Tell the instructions about their input states.
+ * @return whether any input states changed from the previous value
+ * stored in the instruction (if checkChanged is true - else returns
+ * true).
+ */
+ bool giveInputStates();
+ /**
+ * Remove instructions without any input links (and the ones that are
+ * only linked to from a removed instruction).
+ * @return whether any instructions were removed
+ */
+ bool pruneInstructions();
+ /**
+ * Perform optimizations (code cropping, modification, assembly, etc)
+ * based on instruction linkage and processor states.
+ * @return whether anything was changed
+ */
+ bool optimizeInstructions();
+ /**
+ * Redirects any GOTOs that point at the given instruction to the given
+ * label.
+ * @return whether any GOTOs were redirected
+ */
+ bool redirectGotos( Instruction * current, const QString & label );
+ /**
+ * Find out if the given instruction or any of its outputs overwrite
+ * any of the bits of the given register before they are used.
+ */
+ uchar generateRegisterDepends( Instruction * current, const Register & reg );
+ /**
+ * This function should only be used from generateRegisterDepends.
+ * Recursively looks at the output links of the given instruction, and
+ * returns which bits are eventually used before being overwritten.
+ */
+ uchar registerDepends( Instruction * current, const Register & reg );
+ /**
+ * We often need to know whether removing an instruction will affect the
+ * future processor state. This function looks are all possible future
+ * dependencies of the given register, and returns true if the removal
+ * of the instruction will have no critical effect.
+ * @param bitMask only look at the given bits of the register
+ */
+ bool canRemove( Instruction * ins, const Register & reg, uchar bitMask = 0xff );
+
+ Code * m_pCode;
+};
+
+#endif
diff --git a/microbe/parser.cpp b/microbe/parser.cpp
new file mode 100644
index 0000000..15335e0
--- /dev/null
+++ b/microbe/parser.cpp
@@ -0,0 +1,1054 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "btreebase.h"
+#include "expression.h"
+#include "instruction.h"
+#include "parser.h"
+#include "pic14.h"
+#include "traverser.h"
+
+#include <assert.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <qfile.h>
+#include <qregexp.h>
+#include <qstring.h>
+
+#include <iostream>
+using namespace std;
+
+
+//BEGIN class Parser
+Parser::Parser( Microbe * _mb )
+{
+ m_code = 0;
+ m_pPic = 0;
+ mb = _mb;
+ // Set up statement definitions.
+ StatementDefinition definition;
+
+ definition.append( Field(Field::Label, "label") );
+ m_definitionMap["goto"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Label, "label") );
+ m_definitionMap["call"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Expression, "expression") );
+ definition.append( Field(Field::Code, "code") );
+ m_definitionMap["while"] = definition;
+ definition.clear();
+
+ m_definitionMap["end"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Label, "label") );
+ definition.append( Field(Field::Code, "code") );
+ // For backwards compataibility
+ m_definitionMap["sub"] = definition;
+ m_definitionMap["subroutine"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Label, "label") );
+ definition.append( Field(Field::Code, "code") );
+ m_definitionMap["interrupt"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Label, "alias") );
+ definition.append( Field(Field::Label, "dest") );
+ m_definitionMap["alias"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Expression, "expression") );
+ definition.append( Field(Field::FixedString, 0, "then", true) );
+ definition.append( Field(Field::Code, "ifCode") );
+ definition.append( Field(Field::Newline) );
+ definition.append( Field(Field::FixedString, 0, "else", false) );
+ definition.append( Field(Field::Code, "elseCode") );
+ m_definitionMap["if"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Expression, "initExpression") );
+ definition.append( Field(Field::FixedString, 0, "to", true) );
+ definition.append( Field(Field::Expression, "toExpression") );
+ definition.append( Field(Field::FixedString, 0, "step", false) );
+ definition.append( Field(Field::Expression, "stepExpression") );
+ definition.append( Field(Field::Code, "code") );
+ m_definitionMap["for"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Variable, "variable") );
+ m_definitionMap["decrement"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Variable, "variable") );
+ m_definitionMap["increment"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Variable, "variable") );
+ m_definitionMap["rotateleft"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Variable, "variable") );
+ m_definitionMap["rotateright"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Code, "code") );
+ m_definitionMap["asm"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Expression, "expression") );
+ m_definitionMap["delay"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Code, "code") );
+ definition.append( Field(Field::Newline) );
+ definition.append( Field(Field::FixedString, 0, "until", true) );
+ definition.append( Field(Field::Expression, "expression") );
+ m_definitionMap["repeat"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Name, "name") );
+ definition.append( Field(Field::PinList, "pinlist") );
+ m_definitionMap["sevenseg"] = definition;
+ definition.clear();
+
+ definition.append( Field(Field::Name, "name") );
+ definition.append( Field(Field::PinList, "pinlist") );
+ m_definitionMap["keypad"] = definition;
+ definition.clear();
+}
+
+Parser::~Parser()
+{
+}
+
+Parser* Parser::createChildParser()
+{
+ Parser * parser = new Parser( mb );
+
+ return parser;
+}
+
+
+Code * Parser::parseWithChild( const SourceLineList & lines )
+{
+ Parser * p = createChildParser();
+ Code * code = p->parse(lines);
+ delete p;
+ return code;
+}
+
+
+Code * Parser::parse( const SourceLineList & lines )
+{
+ StatementList sList;
+ m_pPic = mb->makePic();
+ m_code = new Code();
+ m_pPic->setCode( m_code );
+ m_pPic->setParser(this);
+ m_bPassedEnd = false;
+
+ /* First pass
+ ==========
+
+ Here we go through the code making each line into a statement object,
+ looking out for braced code as we go, if we find it then we put then
+ we make attach the braced code to the statment.
+ */
+
+ SourceLineList::const_iterator end = lines.end();
+ for ( SourceLineList::const_iterator slit = lines.begin(); slit != end; ++slit )
+ {
+ Statement s;
+ s.content = *slit;
+
+ // Check to see if the line after next is a brace
+ SourceLineList::const_iterator previous = slit;
+ if ( (++slit != end) && (*slit).text() == "{" )
+ s.bracedCode = getBracedCode( & slit, end );
+ else
+ slit = previous;
+
+ if ( !s.text().isEmpty() )
+ sList.append(s);
+ }
+
+ mb->resetDest();
+
+ for( StatementList::Iterator sit = sList.begin(); sit != sList.end(); ++sit )
+ {
+ m_currentSourceLine = (*sit).content;
+
+ QString line = (*sit).text();
+
+ QString command; // e.g. "delay", "for", "subroutine", "increment", etc
+ {
+ int spacepos = line.find(' ');
+ if ( spacepos >= 0 )
+ command = line.left( spacepos );
+ else
+ command = line;
+ }
+ OutputFieldMap fieldMap;
+
+ if ( (*sit).content.line() >= 0 )
+ m_code->append( new Instr_sourceCode( QString("#MSRC\t%1; %2\t%3").arg( (*sit).content.line() + 1 ).arg( (*sit).content.url() ).arg( (*sit).content.text() ) ));
+ bool showBracesInSource = (*sit).hasBracedCode();
+ if ( showBracesInSource )
+ m_code->append(new Instr_sourceCode("{"));
+
+ // Use the first token in the line to look up the statement type
+ DefinitionMap::Iterator dmit = m_definitionMap.find(command);
+ if(dmit == m_definitionMap.end())
+ {
+ if( !processAssignment( (*sit).text() ) )
+ {
+ // Not an assignement, maybe a label
+ if( (*sit).isLabel() )
+ {
+ QString label = (*sit).text().left( (*sit).text().length() - 1 );
+ ///TODO sanity check label name and then do error like "Bad label"
+ m_pPic->Slabel( label );
+ }
+ else
+ mistake( Microbe::Microbe::UnknownStatement );
+ }
+
+ continue; // Give up on the current statement
+ }
+ StatementDefinition definition = dmit.data();
+
+ // Start at the first white space character following the statement name
+ int newPosition = 0;
+ int position = command.length() + 1;
+
+ // Temporaries for use inside the switch
+ Field nextField;
+ Statement nextStatement;
+
+ bool errorInLine = false;
+ bool finishLine = false;
+
+ for( StatementDefinition::Iterator sdit = definition.begin(); sdit != definition.end(); ++sdit )
+ {
+ // If there is an error, or we have finished the statement,
+ // the stop. If we are at the end of a line in a multiline, then
+ // break to fall through to the next line
+ if( errorInLine || finishLine) break;
+
+ Field field = (*sdit);
+ QString token;
+
+ bool saveToken = false;
+ bool saveBraced = false;
+ bool saveSingleLine = false;
+
+ switch(field.type())
+ {
+ case (Field::Label):
+ case (Field::Variable):
+ case (Field::Name):
+ {
+ newPosition = line.find( ' ', position );
+ if(position == newPosition)
+ {
+ newPosition = -1;
+ token = line.mid(position);
+ }
+ else token = line.mid(position, newPosition - position);
+ if( token.isEmpty() )
+ {
+ if(field.type() == Field::Label)
+ mistake( Microbe::Microbe::LabelExpected );
+ else if (field.type() == Field::Variable)
+ mistake( Microbe::VariableExpected );
+ else // field.type() == Field::Name
+ mistake( Microbe::NameExpected );
+ errorInLine = true;
+ continue;
+ }
+ position = newPosition;
+ saveToken = true;
+ break;
+ }
+
+ case (Field::Expression):
+ {
+ // This is slightly different, as there is nothing
+ // in particular that delimits an expression, we just have to
+ // look at what comes next and hope we can use that.
+ StatementDefinition::Iterator it(sdit);
+ ++it;
+ if( it != definition.end() )
+ {
+ nextField = (*it);
+ if(nextField.type() == Field::FixedString)
+ newPosition = line.find(QRegExp("\\b" + nextField.string() + "\\b"));
+ // Although code is not neccessarily braced, after an expression it is the only
+ // sensilbe way to have it.
+ else if(nextField.type() == Field::Code)
+ {
+ newPosition = line.find("{");
+ if(newPosition == -1) newPosition = line.length() + 1;
+ }
+ else if(nextField.type() == Field::Newline)
+ newPosition = line.length()+1;
+ else kdDebug() << "Bad statement definition - awkward field type after expression";
+ }
+ else newPosition = line.length() + 1;
+ if(newPosition == -1)
+ {
+ // Something was missing, we'll just play along for now,
+ // the next iteration will catch whatever was supposed to be there
+ }
+ token = line.mid(position, newPosition - position);
+ position = newPosition;
+ saveToken = true;
+ }
+ break;
+
+ case (Field::PinList):
+ {
+ // For now, just assume that the list of pins will continue to the end of the tokens.
+ // (we could check until we come across a non-pin, but no command has that format at
+ // the moment).
+
+ token = line.mid( position + 1 );
+ position = line.length() + 1;
+ if ( token.isEmpty() )
+ mistake( Microbe::PinListExpected );
+ else
+ saveToken = true;
+
+ break;
+ }
+
+ case (Field::Code):
+ if ( !(*sit).hasBracedCode() )
+ {
+ saveSingleLine = true;
+ token = line.mid(position);
+ position = line.length() + 1;
+ }
+ else if( position != -1 && position <= int(line.length()) )
+ {
+ mistake( Microbe::UnexpectedStatementBeforeBracket );
+ errorInLine = true;
+ continue;
+ }
+ else
+ {
+ // Because of the way the superstructure parsing works there is no
+ // 'next line' as it were, the braced code is attached to the current line.
+ saveBraced = true;
+ }
+ break;
+
+ case (Field::FixedString):
+ {
+ // Is the string found, and is it starting in the right place?
+ int stringPosition = line.find(QRegExp("\\b"+field.string()+"\\b"));
+ if( stringPosition != position || stringPosition == -1 )
+ {
+ if( !field.compulsory() )
+ {
+ position = -1;
+ // Skip the next field
+ ++sdit;
+ continue;
+ }
+ else
+ {
+ // Otherwise raise an appropriate error
+ mistake( Microbe::FixedStringExpected, field.string() );
+ errorInLine = true;
+ continue;
+ }
+ }
+ else
+ {
+ position += field.string().length() + 1;
+ }
+ }
+ break;
+
+ case (Field::Newline):
+ // It looks like the best way to handle this is to just actually
+ // look at the next line, and see if it begins with an expected fixed
+ // string.
+
+ // Assume there is a next field, it would be silly if there weren't.
+ nextField = *(++StatementDefinition::Iterator(sdit));
+ if( nextField.type() == Field::FixedString )
+ {
+ nextStatement = *(++StatementList::Iterator(sit));
+ newPosition = nextStatement.text().find(QRegExp("\\b" + nextField.string() + "\\b"));
+ if(newPosition != 0)
+ {
+ // If the next field is optional just carry on as nothing happened,
+ // the next line will be processed as a new statement
+ if(!nextField.compulsory()) continue;
+
+ }
+ position = 0;
+ line = (*(++sit)).text();
+ m_currentSourceLine = (*sit).content;
+ }
+
+ break;
+
+ case (Field::None):
+ // Do nothing
+ break;
+ }
+
+ if ( saveToken )
+ fieldMap[field.key()] = OutputField( token );
+
+ if ( saveSingleLine )
+ {
+ SourceLineList list;
+ list << SourceLine( token, 0, -1 );
+ fieldMap[field.key()] = OutputField( list );
+ }
+
+ if ( saveBraced )
+ fieldMap[field.key()] = OutputField( (*sit).bracedCode );
+ // If position = -1, we have reached the end of the line.
+ }
+
+ // See if we got to the end of the line, but not all fields had been
+ // processed.
+ if( position != -1 && position <= int(line.length()) )
+ {
+ mistake( Microbe::TooManyTokens );
+ errorInLine = true;
+ }
+
+ if( errorInLine ) continue;
+
+ // Everything has been parsed up, so send it off for processing.
+ processStatement( command, fieldMap );
+
+ if( showBracesInSource )
+ m_code->append(new Instr_sourceCode("}"));
+ }
+
+ delete m_pPic;
+ return m_code;
+}
+
+bool Parser::processAssignment(const QString &line)
+{
+ QStringList tokens = Statement::tokenise(line);
+
+ // Have to have at least 3 tokens for an assignment;
+ if ( tokens.size() < 3 )
+ return false;
+
+ QString firstToken = tokens[0];
+
+ firstToken = mb->alias(firstToken);
+ // Well firstly we look to see if it is a known variable.
+ // These can include 'special' variables such as ports
+ // For now, the processor subclass generates ports it self
+ // and puts them in a list for us.
+
+
+ // Look for port variables first.
+ if ( firstToken.contains(".") )
+ {
+ PortPin portPin = m_pPic->toPortPin( firstToken );
+
+ // check port is valid
+ if ( portPin.pin() == -1 )
+ mistake( Microbe::InvalidPort, firstToken );
+ // more error checking
+ if ( tokens[1] != "=" )
+ mistake( Microbe::UnassignedPin );
+
+ QString state = tokens[2];
+ if( state == "high" )
+ m_pPic->Ssetlh( portPin, true );
+ else if( state == "low" )
+ m_pPic->Ssetlh( portPin, false );
+ else
+ mistake( Microbe::NonHighLowPinState );
+ }
+ // no dots, lets try for just a port name
+ else if( m_pPic->isValidPort( firstToken ) )
+ {
+ // error checking
+ if ( tokens[1] != "=" )
+ mistake( Microbe::UnassignedPort, tokens[1] );
+
+ Expression( m_pPic, mb, m_currentSourceLine, false ).compileExpression(line.mid(line.find("=")+1));
+ m_pPic->saveResultToVar( firstToken );
+ }
+ else if ( m_pPic->isValidTris( firstToken ) )
+ {
+ if( tokens[1] == "=" )
+ {
+ Expression( m_pPic, mb, m_currentSourceLine, false ).compileExpression(line.mid(line.find("=")+1));
+ m_pPic->Stristate(firstToken);
+ }
+ }
+ else
+ {
+ // Is there an assignment?
+ if ( tokens[1] != "=" )
+ return false;
+
+ if ( !mb->isValidVariableName( firstToken ) )
+ {
+ mistake( Microbe::InvalidVariableName, firstToken );
+ return true;
+ }
+
+ // Don't care whether or not the variable is new; Microbe will only add it if it
+ // hasn't been defined yet.
+ mb->addVariable( Variable( Variable::charType, firstToken ) );
+
+ Expression( m_pPic, mb, m_currentSourceLine, false ).compileExpression(line.mid(line.find("=")+1));
+
+ Variable v = mb->variable( firstToken );
+ switch ( v.type() )
+ {
+ case Variable::charType:
+ m_pPic->saveResultToVar( v.name() );
+ break;
+
+ case Variable::keypadType:
+ mistake( Microbe::ReadOnlyVariable, v.name() );
+ break;
+
+ case Variable::sevenSegmentType:
+ m_pPic->SsevenSegment( v );
+ break;
+
+ case Variable::invalidType:
+ // Doesn't happen, but include this case to avoid compiler warnings
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+SourceLineList Parser::getBracedCode( SourceLineList::const_iterator * it, SourceLineList::const_iterator end )
+{
+ // Note: The sourceline list has the braces on separate lines.
+
+ // This function should only be called when the parser comes across a line that is a brace.
+ assert( (**it).text() == "{" );
+
+ SourceLineList braced;
+
+ // Jump past the first brace
+ unsigned level = 1;
+ ++(*it);
+
+ for ( ; *it != end; ++(*it) )
+ {
+ if ( (**it).text() == "{" )
+ level++;
+
+ else if ( (**it).text() == "}" )
+ level--;
+
+ if ( level == 0 )
+ return braced;
+
+ braced << **it;
+ }
+
+ // TODO Error: mismatched bracing
+ return braced;
+}
+
+
+void Parser::processStatement( const QString & name, const OutputFieldMap & fieldMap )
+{
+ // Name is guaranteed to be something known, the calling
+ // code has taken care of that. Also fieldMap is guaranteed to contain
+ // all required fields.
+
+ if ( name == "goto" )
+ m_pPic->Sgoto(fieldMap["label"].string());
+
+ else if ( name == "call" )
+ m_pPic->Scall(fieldMap["label"].string());
+
+ else if ( name == "while" )
+ m_pPic->Swhile( parseWithChild(fieldMap["code"].bracedCode() ), fieldMap["expression"].string() );
+
+ else if ( name == "repeat" )
+ m_pPic->Srepeat( parseWithChild(fieldMap["code"].bracedCode() ), fieldMap["expression"].string() );
+
+ else if ( name == "if" )
+ m_pPic->Sif(
+ parseWithChild(fieldMap["ifCode"].bracedCode() ),
+ parseWithChild(fieldMap["elseCode"].bracedCode() ),
+ fieldMap["expression"].string() );
+
+ else if ( name == "sub" || name == "subroutine" )
+ {
+ if(!m_bPassedEnd)
+ {
+ mistake( Microbe::InterruptBeforeEnd );
+ }
+ else
+ {
+ m_pPic->Ssubroutine( fieldMap["label"].string(), parseWithChild( fieldMap["code"].bracedCode() ) );
+ }
+ }
+ else if( name == "interrupt" )
+ {
+ QString interrupt = fieldMap["label"].string();
+
+ if(!m_bPassedEnd)
+ {
+ mistake( Microbe::InterruptBeforeEnd );
+ }
+ else if( !m_pPic->isValidInterrupt( interrupt ) )
+ {
+ mistake( Microbe::InvalidInterrupt );
+ }
+ else if ( mb->isInterruptUsed( interrupt ) )
+ {
+ mistake( Microbe::InterruptRedefined );
+ }
+ else
+ {
+ mb->setInterruptUsed( interrupt );
+ m_pPic->Sinterrupt( interrupt, parseWithChild( fieldMap["code"].bracedCode() ) );
+ }
+ }
+ else if( name == "end" )
+ {
+ ///TODO handle end if we are not in the top level
+ m_bPassedEnd = true;
+ m_pPic->Send();
+ }
+ else if( name == "for" )
+ {
+ QString step = fieldMap["stepExpression"].string();
+ bool stepPositive;
+
+ if( fieldMap["stepExpression"].found() )
+ {
+ if(step.left(1) == "+")
+ {
+ stepPositive = true;
+ step = step.mid(1).stripWhiteSpace();
+ }
+ else if(step.left(1) == "-")
+ {
+ stepPositive = false;
+ step = step.mid(1).stripWhiteSpace();
+ }
+ else stepPositive = true;
+ }
+ else
+ {
+ step = "1";
+ stepPositive = true;
+ }
+
+ QString variable = fieldMap["initExpression"].string().mid(0,fieldMap["initExpression"].string().find("=")).stripWhiteSpace();
+ QString endExpr = variable+ " <= " + fieldMap["toExpression"].string().stripWhiteSpace();
+
+ if( fieldMap["stepExpression"].found() )
+ {
+ bool isConstant;
+ step = processConstant(step,&isConstant);
+ if( !isConstant )
+ mistake( Microbe::NonConstantStep );
+ }
+
+ SourceLineList tempList;
+ tempList << SourceLine( fieldMap["initExpression"].string(), 0, -1 );
+
+ m_pPic->Sfor( parseWithChild( fieldMap["code"].bracedCode() ), parseWithChild( tempList ), endExpr, variable, step, stepPositive );
+ }
+ else if( name == "alias" )
+ {
+ // It is important to get this the right way round!
+ // The alias should be the key since two aliases could
+ // point to the same name.
+
+ QString alias = fieldMap["alias"].string().stripWhiteSpace();
+ QString dest = fieldMap["dest"].string().stripWhiteSpace();
+
+ // Check to see whether or not we've already aliased it...
+// if ( mb->alias(alias) != alias )
+// mistake( Microbe::AliasRedefined );
+// else
+ mb->addAlias( alias, dest );
+ }
+ else if( name == "increment" )
+ {
+ QString variableName = fieldMap["variable"].string();
+
+ if ( !mb->isVariableKnown( variableName ) )
+ mistake( Microbe::UnknownVariable );
+ else if ( !mb->variable( variableName ).isWritable() )
+ mistake( Microbe::ReadOnlyVariable, variableName );
+ else
+ m_pPic->SincVar( variableName );
+ }
+ else if( name == "decrement" )
+ {
+ QString variableName = fieldMap["variable"].string();
+
+ if ( !mb->isVariableKnown( variableName ) )
+ mistake( Microbe::UnknownVariable );
+ else if ( !mb->variable( variableName ).isWritable() )
+ mistake( Microbe::ReadOnlyVariable, variableName );
+ else
+ m_pPic->SdecVar( variableName );
+ }
+ else if( name == "rotateleft" )
+ {
+ QString variableName = fieldMap["variable"].string();
+
+ if ( !mb->isVariableKnown( variableName ) )
+ mistake( Microbe::UnknownVariable );
+ else if ( !mb->variable( variableName ).isWritable() )
+ mistake( Microbe::ReadOnlyVariable, variableName );
+ else
+ m_pPic->SrotlVar( variableName );
+ }
+ else if( name == "rotateright" )
+ {
+ QString variableName = fieldMap["variable"].string();
+
+ if ( !mb->isVariableKnown( variableName ) )
+ mistake( Microbe::UnknownVariable );
+ else if ( !mb->variable( variableName ).isWritable() )
+ mistake( Microbe::ReadOnlyVariable, variableName );
+ else
+ m_pPic->SrotrVar( variableName );
+ }
+ else if( name == "asm" )
+ {
+ m_pPic->Sasm( SourceLine::toStringList( fieldMap["code"].bracedCode() ).join("\n") );
+ }
+ else if( name == "delay" )
+ {
+ // This is one of the rare occasions that the number will be bigger than a byte,
+ // so suppressNumberTooBig must be used
+ bool isConstant;
+ QString delay = processConstant(fieldMap["expression"].string(),&isConstant,true);
+ if (!isConstant)
+ mistake( Microbe::NonConstantDelay );
+// else m_pPic->Sdelay( fieldMap["expression"].string(), "");
+ else
+ {
+ // TODO We should use the "delay" string returned by processConstant - not the expression (as, e.g. 2*3 won't be ok)
+ int length_ms = literalToInt( fieldMap["expression"].string() );
+ if ( length_ms >= 0 )
+ m_pPic->Sdelay( length_ms * 1000 ); // Pause the delay length in microseconds
+ else
+ mistake( Microbe::NonConstantDelay );
+ }
+ }
+ else if ( name == "keypad" || name == "sevenseg" )
+ {
+ QStringList pins = QStringList::split( ' ', fieldMap["pinlist"].string() );
+ QString variableName = fieldMap["name"].string();
+
+ if ( mb->isVariableKnown( variableName ) )
+ {
+ mistake( Microbe::VariableRedefined, variableName );
+ return;
+ }
+
+ PortPinList pinList;
+
+ QStringList::iterator end = pins.end();
+ for ( QStringList::iterator it = pins.begin(); it != end; ++it )
+ {
+ PortPin portPin = m_pPic->toPortPin(*it);
+ if ( portPin.pin() == -1 )
+ {
+ // Invalid port/pin
+ //TODO mistake
+ return;
+ }
+ pinList << portPin;
+ }
+
+ if ( name == "keypad" )
+ {
+ Variable v( Variable::keypadType, variableName );
+ v.setPortPinList( pinList );
+ mb->addVariable( v );
+ }
+
+ else // name == "sevenseg"
+ {
+ if ( pinList.size() != 7 )
+ mistake( Microbe::InvalidPinMapSize );
+ else
+ {
+ Variable v( Variable::sevenSegmentType, variableName );
+ v.setPortPinList( pinList );
+ mb->addVariable( v );
+ }
+ }
+ }
+}
+
+
+void Parser::mistake( Microbe::MistakeType type, const QString & context )
+{
+ mb->compileError( type, context, m_currentSourceLine );
+}
+
+
+// static function
+QStringList Statement::tokenise(const QString &line)
+{
+ QStringList result;
+ QString current;
+ int count = 0;
+
+ for(int i = 0; i < int(line.length()); i++)
+ {
+ QChar nextChar = line[i];
+ if( nextChar.isSpace() )
+ {
+ if( count > 0 )
+ {
+ result.append(current);
+ current = "";
+ count = 0;
+ }
+ }
+ else if( nextChar == '=' )
+ {
+ if( count > 0 ) result.append(current);
+ current = "";
+ count = 0;
+ result.append("=");
+ }
+ else if( nextChar == '{' )
+ {
+ if( count > 0 ) result.append(current);
+ current = "";
+ count = 0;
+ result.append("{");
+ }
+ else
+ {
+ count++;
+ current.append(nextChar);
+ }
+ }
+ if( count > 0 ) result.append(current);
+ return result;
+}
+
+int Parser::doArithmetic(int lvalue, int rvalue, Expression::Operation op)
+{
+ switch(op)
+ {
+ case Expression::noop: return 0;
+ case Expression::addition: return lvalue + rvalue;
+ case Expression::subtraction: return lvalue - rvalue;
+ case Expression::multiplication: return lvalue * rvalue;
+ case Expression::division: return lvalue / rvalue;
+ case Expression::exponent: return lvalue ^ rvalue;
+ case Expression::equals: return lvalue == rvalue;
+ case Expression::notequals: return !(lvalue == rvalue);
+ case Expression::bwand: return lvalue & rvalue;
+ case Expression::bwor: return lvalue | rvalue;
+ case Expression::bwxor: return lvalue ^ rvalue;
+ case Expression::bwnot: return !rvalue;
+ case Expression::le: return lvalue <= rvalue;
+ case Expression::ge: return lvalue >= rvalue;
+ case Expression::lt: return lvalue < rvalue;
+ case Expression::gt: return lvalue > rvalue;
+
+ case Expression::pin:
+ case Expression::notpin:
+ case Expression::function:
+ case Expression::divbyzero:
+ case Expression::read_keypad:
+ // Not applicable actions.
+ break;
+ }
+ return -1;
+}
+
+bool Parser::isLiteral( const QString &text )
+{
+ bool ok;
+ literalToInt( text, & ok );
+ return ok;
+}
+
+/*
+Literal's in form:
+-> 321890
+-> 021348
+-> 0x3C
+-> b'0100110'
+-> 0101001b
+-> h'43A'
+-> 2Ah
+
+Everything else is non-literal...
+*/
+int Parser::literalToInt( const QString &literal, bool * ok )
+{
+ bool temp;
+ if ( !ok )
+ ok = & temp;
+ *ok = true;
+
+ int value = -1;
+
+ // Note when we use toInt, we don't have to worry about checking
+ // that literal.mid() is convertible, as toInt returns this in ok anyway.
+
+ // Try binary first, of form b'n...n'
+ if( literal.left(2) == "b'" && literal.right(1) == "'" )
+ {
+ value = literal.mid(2,literal.length() - 3).toInt(ok,2);
+ return *ok ? value : -1;
+ }
+
+ // Then try hex of form h'n...n'
+ if( literal.left(2) == "h'" && literal.right(1) == "'" )
+ {
+ value = literal.mid(2,literal.length() - 3).toInt(ok,16);
+ return *ok ? value : -1;
+ }
+
+ // otherwise, let QString try and convert it
+ // base 0 == automatic base guessing
+ value = literal.toInt( ok, 0 );
+ return *ok ? value : -1;
+}
+
+
+void Parser::compileConditionalExpression( const QString & expression, Code * ifCode, Code * elseCode ) const
+{
+ ///HACK ///TODO this is a little improper, I don't think we should be using the pic that called us...
+
+ Expression( m_pPic, mb, m_currentSourceLine, false ).compileConditional(expression,ifCode,elseCode);
+}
+
+
+QString Parser::processConstant(const QString &expression, bool * isConstant, bool suppressNumberTooBig) const
+{
+ return Expression( m_pPic, mb, m_currentSourceLine, suppressNumberTooBig ).processConstant(expression, isConstant);
+}
+//END class Parser
+
+
+
+//BEGIN class Field
+Field::Field()
+{
+ m_type = None;
+ m_compulsory = false;
+}
+
+
+Field::Field( Type type, const QString & key )
+{
+ m_type = type;
+ m_compulsory = false;
+ m_key = key;
+}
+
+
+Field::Field( Type type, const QString & key, const QString & string, bool compulsory )
+{
+ m_type = type;
+ m_compulsory = compulsory;
+ m_key = key;
+ m_string = string;
+}
+//END class Field
+
+
+
+//BEGIN class OutputField
+OutputField::OutputField()
+{
+ m_found = false;
+}
+
+
+OutputField::OutputField( const SourceLineList & bracedCode )
+{
+ m_bracedCode = bracedCode;
+ m_found = true;
+}
+
+OutputField::OutputField( const QString & string/*, int lineNumber*/ )
+{
+ m_string = string;
+ m_found = true;
+}
+//END class OutputField
+
+
+
+#if 0
+// Second pass
+
+ else if( firstToken == "include" )
+ {
+ // only cope with 'sane' strings a.t.m.
+ // e.g. include "filename.extenstion"
+ QString filename = (*sit).content.mid( (*sit).content.find("\"") ).stripWhiteSpace();
+ // don't strip whitespace from within quotes as you
+ // can have filenames composed entirely of spaces (kind of weird)...
+ // remove quotes.
+ filename = filename.mid(1);
+ filename = filename.mid(0,filename.length()-1);
+ QFile includeFile(filename);
+ if( includeFile.open(IO_ReadOnly) )
+ {
+ QTextStream stream( &includeFile );
+ QStringList includeCode;
+ while( !stream.atEnd() )
+ {
+ includeCode += stream.readLine();
+ }
+ ///TODO make includes work
+ //output += parse(includeCode);
+ includeFile.close();
+ }
+ else
+ mistake( Microbe::UnopenableInclude, filename );
+ }
+#endif
+
+
diff --git a/microbe/parser.h b/microbe/parser.h
new file mode 100644
index 0000000..ece433d
--- /dev/null
+++ b/microbe/parser.h
@@ -0,0 +1,293 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef PARSER_H
+#define PARSER_H
+
+#include "expression.h"
+#include "instruction.h"
+#include "microbe.h"
+
+#include "qmap.h"
+#include "qvaluelist.h"
+
+class PIC14;
+
+/**
+@author Daniel Clarke
+@author David Saxton
+*/
+class Statement
+{
+ public:
+ /**
+ * Is the assembly output generated for this statement.
+ */
+ InstructionList * code;
+ /**
+ * The original microbe source line.
+ */
+ SourceLine content;
+ /**
+ * Returns the microbe code from content.
+ */
+ QString text() const { return content.text(); }
+ /**
+ * If this Statement is for a for loop, then content will contain
+ * something like "for x = 1 to 10", and bracedCode will contain the
+ * source code within (but not including) the braces.
+ */
+ SourceLineList bracedCode;
+ /**
+ * Just returns whether or not the braced code is empty.
+ */
+ bool hasBracedCode() const { return !bracedCode.isEmpty(); }
+ /**
+ * This breaks up the line seperated by spaces,{,and =/
+ */
+ static QStringList tokenise(const QString &line);
+ /**
+ * @see tokenise(const QString &line)
+ */
+ QStringList tokenise() const { return tokenise( content.text() ); }
+ /**
+ * @returns whether or not the content looks like a label (ends with a
+ * colon).
+ */
+ bool isLabel() const { return content.text().right(1) == ":"; }
+};
+
+typedef QValueList<Statement> StatementList;
+
+/**
+@author Daniel Clarke
+@author David Saxton
+*/
+class Field
+{
+ public:
+ enum Type
+ {
+ // String that doesn't change the program logic, but might or might
+ // not need to be there depending on the statement (e.g. "then" in
+ // the if-statement).
+ FixedString,
+
+ // Label, Variable, Name are all treated similarly (only different
+ // error messages are given).
+ Label, // e.g. in "goto [Label]"
+ Variable, // e.g. in "increment [Variable]"
+ Name, // e.g. in "sevenseg [Name]"
+
+ // List of strings which should be pin names.
+ PinList,
+
+ // Braced code.
+ Code,
+
+ Expression,
+ Newline,
+ None
+ };
+
+ /**
+ * Create a Field of type None.
+ */
+ Field();
+ /**
+ * Create a Field.
+ */
+ Field( Type type, const QString & key = 0 );
+ /**
+ * Create a Field (this constructor should only be used with
+ * FixedStrings.
+ */
+ Field( Type type, const QString & key, const QString & string, bool compulsory = true);
+
+ /**
+ * The type of field expected.
+ */
+ Type type() const { return m_type; }
+ /**
+ * String data relevant to the field dependent on m_type.
+ */
+ QString string() const { return m_string; }
+ /**
+ * The key in which the found token will be attached to
+ * in the output map. If it is an empty string, then the field will be
+ * processed but not put in the output, effectively ignoring it.
+ */
+ QString key() const { return m_key; }
+ /**
+ * Only FixedStrings may be compulsory, that is the only type that can
+ * actually have its presence checked.
+ * This flag is set to indicate that no error should be rasied if the
+ * field is not present. Note that if a field is found missing, then
+ * the rest of the statement is ignored (regardless of whether the rest
+ * is marked compulsory or not.)
+ */
+ bool compulsory() const { return m_compulsory; }
+
+ private:
+ Type m_type;
+ QString m_string;
+ QString m_key;
+ bool m_compulsory;
+};
+
+
+class OutputField
+{
+ public:
+ /**
+ * Constructs an empty output field.
+ */
+ OutputField();
+ /**
+ * Constructs an output field consisting of braced code.
+ */
+ OutputField( const SourceLineList & bracedCode );
+ /**
+ * Constructs an output field consisting of a single string.
+ */
+ OutputField( const QString &string );
+
+ QString string() const { return m_string; }
+ SourceLineList bracedCode() const { return m_bracedCode; }
+ bool found() const { return m_found; }
+
+ private:
+ QString m_string;
+ SourceLineList m_bracedCode;
+ /**
+ * This specifies if a non compulsory field was found or not.
+ */
+ bool m_found;
+};
+
+typedef QValueList<Field> StatementDefinition;
+typedef QMap<QString,StatementDefinition> DefinitionMap;
+typedef QMap<QString,OutputField> OutputFieldMap;
+
+
+/**
+@author Daniel Clarke
+@author David Saxton
+*/
+class Parser
+{
+ public:
+ Parser( Microbe * mb );
+ ~Parser();
+
+ /**
+ * Report a compile error to Microbe; the current source line will be
+ * sent. Context is extra information to be inserted into the error
+ * message, only applicable to some errors (such as a use of a reserved
+ * keyword).
+ */
+ void mistake( Microbe::MistakeType type, const QString & context = 0 );
+ /**
+ * Creates a new instance of the parser class with all state information
+ * (class members) copied from this instance of the class. Don't forget to
+ * delete it when you are done!
+ */
+ Parser * createChildParser();
+ /**
+ * Creates a child class and uses it to parse recursively.
+ */
+ Code * parseWithChild( const SourceLineList & lines );
+ /**
+ * This is the actual parsing function, make sure to use parseUsingChild
+ * instead (???)
+ */
+ Code * parse( const SourceLineList & lines );
+ /**
+ * Returns the lines between the braces, excluding the braces, e.g.
+ * defproc name
+ * {
+ * more code
+ * some more code
+ * }
+ * returns ("more code","some more code").
+ * Note that Microbe has already put the braces on separate lines for us.
+ * @param it is the iterator at the position of the first brace, this
+ * function will return with it pointing at the matching closing brace.
+ * @param end is the iterator pointing to the end of the source line
+ * list, so that we don't search past it.
+ * @returns The braced code (excluding the braces).
+ */
+ SourceLineList getBracedCode( SourceLineList::const_iterator * it, SourceLineList::const_iterator end );
+ /**
+ * Returns expression type.
+ * 0 = directly usable number (literal).
+ * 1 = variable.
+ * 2 = expression that needs evaluating.
+ */
+ ExprType getExpressionType( const QString & expression );
+ /**
+ * Examines the text to see if it looks like a literal, i.e. of the form
+ * "321890","021348","0x3C","b'0100110'","0101001b","h'43A'", or "2Ah".
+ * Everything else is considered non-literal.
+ * @see literalToInt.
+ */
+ static bool isLiteral( const QString &text );
+ /**
+ * Tries to convert the given literal string into a integer. If it fails,
+ * i.e. it is not any recognised literal, then it returns -1 and sets *ok to
+ * false. Else, *ok is set to true and the literal value is returned.
+ * @see isLiteral
+ */
+ static int literalToInt( const QString & literal, bool * ok = 0l );
+ /**
+ * Does the specified operation on the given numbers and returns the result.
+ */
+ static int doArithmetic( int lvalue, int rvalue, Expression::Operation op );
+ /**
+ * @return whether it was an assignment (which might not have been in
+ * the proper form).
+ */
+ bool processAssignment(const QString &line);
+
+ void compileConditionalExpression( const QString & expression, Code * ifCode, Code * elseCode ) const;
+ QString processConstant(const QString &expression, bool * isConstant, bool suppressNumberTooBig = false) const;
+
+ private:
+ /**
+ * This is called when the bulk of the actual parsing has been carried
+ * out and is ready to be turned into assembly code.
+ * @param name Name of the statement to be processed
+ * @param fieldMap A map of named fields as appropriate to the statement
+ */
+ void processStatement( const QString & name, const OutputFieldMap & fieldMap );
+
+ DefinitionMap m_definitionMap;
+ PIC14 * m_pPic;
+ bool m_bPassedEnd;
+ Microbe * mb;
+ Code * m_code;
+ SourceLine m_currentSourceLine;
+
+ private: // Disable copy constructor and operator=
+ Parser( const Parser & );
+ Parser &operator=( const Parser & );
+};
+
+#endif
diff --git a/microbe/pic14.cpp b/microbe/pic14.cpp
new file mode 100644
index 0000000..7785afb
--- /dev/null
+++ b/microbe/pic14.cpp
@@ -0,0 +1,1196 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+
+#include "instruction.h"
+#include "parser.h"
+#include "pic14.h"
+
+#include <assert.h>
+#include <kdebug.h>
+#include <iostream>
+using namespace std;
+
+bool LEDSegTable[][7] = {
+{ 1, 1, 1, 1, 1, 1, 0 },
+{ 0, 1, 1, 0, 0, 0, 0 }, // 1
+{ 1, 1, 0, 1, 1, 0, 1 }, // 2
+{ 1, 1, 1, 1, 0, 0, 1 }, // 3
+{ 0, 1, 1, 0 ,0, 1, 1 }, // 4
+{ 1, 0, 1, 1, 0, 1, 1 }, // 5
+{ 1, 0, 1, 1, 1, 1, 1 }, // 6
+{ 1, 1, 1, 0, 0, 0, 0 }, // 7
+{ 1, 1, 1, 1, 1, 1, 1 }, // 8
+{ 1, 1, 1, 0, 0, 1, 1 }, // 9
+{ 1, 1, 1, 0, 1, 1, 1 }, // A
+{ 0, 0, 1, 1, 1, 1, 1 }, // b
+{ 1, 0, 0, 1, 1, 1, 0 }, // C
+{ 0, 1, 1, 1, 1, 0, 1 }, // d
+{ 1, 0, 0, 1, 1, 1, 1 }, // E
+{ 1, 0, 0, 0, 1, 1, 1 } // F
+};
+
+
+
+PIC14::PIC14( Microbe * master, Type type )
+{
+ mb = master;
+ m_pCode = 0l;
+ m_type = type;
+
+}
+
+
+PIC14::~PIC14()
+{
+}
+
+PortPin PIC14::toPortPin( const QString & portPinString )
+{
+ QString port;
+ int pin = -1;
+
+ // In form e.g. RB3
+ if ( portPinString.length() == 3 )
+ {
+ port = QString("PORT%1").arg( portPinString[1].upper() );
+ pin = QString( portPinString[2] ).toInt();
+ }
+ else
+ {
+ int dotpos = portPinString.find(".");
+ if ( dotpos == -1 )
+ return PortPin();
+
+ port = portPinString.left(dotpos);
+ pin = portPinString.mid(dotpos+1).toInt();
+ }
+
+ PortPin portPin( port, pin );
+
+ if ( isValidPortPin( portPin ) )
+ return portPin;
+ else
+ return PortPin();
+}
+
+
+void PIC14::mergeCode( Code * code )
+{
+ m_pCode->merge( code );
+}
+
+
+uchar PIC14::gprStart() const
+{
+ switch ( m_type )
+ {
+ case P16C84:
+ case P16F84:
+ return 0xc;
+
+ case P16F627:
+ case P16F628:
+ return 0x20;
+
+ case unknown:
+ break;
+ }
+
+ kdError() << k_funcinfo << "Unknown PIC type = " << m_type << endl;
+ return 0xc;
+}
+
+
+PIC14::Type PIC14::toType( const QString & _text )
+{
+ QString text = _text.upper().simplifyWhiteSpace().remove('P');
+
+ if ( text == "16C84" )
+ return P16C84;
+
+ if ( text == "16F84" )
+ return P16F84;
+
+ if ( text == "16F627" )
+ return P16F627;
+
+ if ( text == "16F628" )
+ return P16F628;
+
+ cerr << QString("%1 is not a known PIC identifier\n").arg(_text);
+ return unknown;
+}
+
+
+QString PIC14::minimalTypeString() const
+{
+ switch ( m_type )
+ {
+ case P16C84:
+ return "16C84";
+
+ case P16F84:
+ return "16F84";
+
+ case P16F627:
+ return "16F627";
+
+ case P16F628:
+ return "16F628";
+
+ case unknown:
+ break;
+ }
+
+ kdError() << k_funcinfo << "Unknown PIC type = " << m_type << endl;
+ return 0;;
+}
+
+
+void PIC14::postCompileConstruct( const QStringList &interrupts )
+{
+ m_pCode->append( new Instr_raw("\n\tEND\n"), Code::Subroutine );
+
+ if ( interrupts.isEmpty() )
+ {
+ // If there are no ISRs then we don't need to put in any handler code.
+ // Instead, just insert the goto start instruction in case we need to
+ // jump past any lookup tabes (and if there are none, then the optimizer
+ // will remove the goto instruction).
+ m_pCode->append(new Instr_goto("_start"), Code::InterruptHandler);
+ m_pCode->queueLabel( "_start", Code::LookupTable );
+ return;
+ }
+
+ /*
+ INTCON register:
+ 7 --- GIE EEIE T0IE INTE RBIE T0IF INTF RBIF --- 0
+
+ E: enable
+ F: flag
+ Flag bits must be cleared manually before reactivating GIE,
+ but we do this in each individual interrupt handler
+ */
+
+ // The bizarre dance with swap is to ensure the status bits
+ // are preserved properly
+ m_pCode->append(new Instr_goto("_start"), Code::InterruptHandler);
+
+ m_pCode->append(new Instr_raw("ORG 0x4"), Code::InterruptHandler);
+ // When we arrive here:
+ // Return address on stack,
+ // GIE flag cleared (globally interrupts disabled)
+ // W or STATUS not preserved by processor.
+ m_pCode->append(new Instr_movwf("W_TEMP"), Code::InterruptHandler);
+ m_pCode->append(new Instr_swapf("STATUS",0), Code::InterruptHandler);
+ m_pCode->append(new Instr_movwf("STATUS_TEMP"), Code::InterruptHandler);
+
+ QStringList::ConstIterator interruptsEnd = interrupts.end();
+ for( QStringList::ConstIterator it = interrupts.begin(); it != interruptsEnd; ++it )
+ {
+ // Is the interrupt's flag bit set?
+ m_pCode->append(new Instr_btfsc("INTCON",QString::number(interruptNameToBit((*it), true))), Code::InterruptHandler);
+ m_pCode->append(new Instr_goto("_interrupt_" + (*it)), Code::InterruptHandler); // Yes, do its handler routine
+ // Otherwise fall through to the next.
+ }
+
+ // If there was "somehow" a suprious interrupt there isn't really
+ // much we can do about that (??) so just fall through and hope for the worst.
+
+ m_pCode->queueLabel( "_interrupt_end", Code::InterruptHandler );
+ m_pCode->append(new Instr_swapf("STATUS_TEMP",0), Code::InterruptHandler );
+ m_pCode->append(new Instr_movwf("STATUS"), Code::InterruptHandler );
+ m_pCode->append(new Instr_swapf("W_TEMP",1), Code::InterruptHandler );
+ m_pCode->append(new Instr_swapf("W_TEMP",0), Code::InterruptHandler );
+ m_pCode->append(new Instr_retfie()); // Returns and renables globally interrupts.
+
+ m_pCode->queueLabel( "_start", Code::LookupTable );
+}
+
+int PIC14::interruptNameToBit(const QString &name, bool flag)
+{
+ // 7 --- GIE EEIE T0IE INTE RBIE T0IF INTF RBIF --- 0
+
+ if( name == "change" ) // RB
+ {
+ if(flag) return 0;
+ else return 3;
+ }
+ else if( name == "timer" )
+ {
+ if(flag) return 2;
+ else return 5;
+ }
+ else if( name == "external" )
+ {
+ if(flag) return 1;
+ else return 4;
+ }
+
+ return -1;
+}
+
+
+bool PIC14::isValidPort( const QString & portName ) const
+{
+ return ( portName == "PORTA" || portName == "porta" ||
+ portName == "PORTB" || portName == "portb" );
+}
+
+
+bool PIC14::isValidPortPin( const PortPin & portPin ) const
+{
+ if ( portPin.port() == "PORTA" )
+ return (portPin.pin() >= 0) && (portPin.pin() <= 4);
+
+ if ( portPin.port() == "PORTB" )
+ return (portPin.pin() >= 0) && (portPin.pin() <= 7);
+
+ return false;
+}
+
+
+bool PIC14::isValidTris( const QString & trisName ) const
+{
+ return ( trisName == "TRISA" || trisName == "trisa" ||
+ trisName == "TRISB" || trisName == "trisb" );
+}
+
+
+bool PIC14::isValidInterrupt( const QString & interruptName ) const
+{
+ if(m_type == "P16F84" || m_type =="P16C84")
+ {
+ return ( interruptName == "change" ||
+ interruptName == "timer" ||
+ interruptName == "external" );
+ }
+ return false;
+}
+
+
+void PIC14::setConditionalCode( Code * ifCode, Code * elseCode )
+{
+ m_ifCode = ifCode;
+ m_elseCode = elseCode;
+}
+
+void PIC14::Sgoto(const QString &label)
+{
+ m_pCode->append( new Instr_goto(label) );
+}
+
+void PIC14::Slabel(const QString &label)
+{
+// std::cout << k_funcinfo << "label="<<label<<'\n';
+ m_pCode->queueLabel( label, Code::Middle );
+}
+
+void PIC14::Send()
+{
+ m_pCode->append( new Instr_sleep() );
+}
+
+void PIC14::Ssubroutine( const QString &procName, Code * subCode )
+{
+ m_pCode->queueLabel( procName, Code::Subroutine );
+ m_pCode->merge( subCode, Code::Subroutine );
+ m_pCode->append( new Instr_return(), Code::Subroutine );
+}
+
+void PIC14::Sinterrupt( const QString &procName, Code * subCode )
+{
+ m_pCode->queueLabel( "_interrupt_" + procName, Code::Subroutine );
+
+ // Clear the interrupt flag for this particular interrupt source
+ m_pCode->append( new Instr_bcf("INTCON",QString::number(interruptNameToBit(procName,true))) );
+ m_pCode->merge( subCode, Code::Subroutine );
+
+ m_pCode->append( new Instr_goto("_interrupt_end"), Code::Subroutine );
+}
+
+
+void PIC14::Scall(const QString &name)
+{
+ m_pCode->append( new Instr_call(name) );
+}
+
+
+void PIC14::Ssetlh( const PortPin & portPin, bool high)
+{
+ if(high)
+ m_pCode->append( new Instr_bsf( portPin.port(),QString::number(portPin.pin()) ) );
+ else
+ m_pCode->append( new Instr_bcf( portPin.port(), QString::number(portPin.pin()) ) );
+}
+
+void PIC14::rearrangeOpArguments( QString * val1, QString * val2, LocationType * val1Type, LocationType * val2Type)
+{
+ if( *val2Type == work && *val1Type != work )
+ {
+ LocationType tempType = *val2Type;
+ QString tempVal = *val2;
+
+ *val2Type = *val1Type;
+ *val2 = *val1;
+
+ *val1Type = tempType;
+ *val1 = tempVal;
+ }
+}
+
+void PIC14::add( QString val1, QString val2, LocationType val1Type, LocationType val2Type )
+{
+ rearrangeOpArguments( &val1, &val2, &val1Type, &val2Type );
+
+ switch(val1Type)
+ {
+ case num: m_pCode->append(new Instr_movlw( val1.toInt( 0, 0 ) )); break;
+ case work: break;
+ case var: m_pCode->append(new Instr_movf(val1,0)); break;
+ }
+
+ switch(val2Type)
+ {
+ case num: m_pCode->append(new Instr_addlw(val2.toInt( 0, 0 ))); break;
+ case work: break;
+ case var: m_pCode->append(new Instr_addwf(val2,0)); break;
+ }
+}
+
+void PIC14::subtract( const QString & val1, const QString & val2, LocationType val1Type, LocationType val2Type )
+{
+ switch(val2Type)
+ {
+ case num: m_pCode->append(new Instr_movlw( val2.toInt( 0, 0 ) )); break;
+ case work: break;
+ case var: m_pCode->append(new Instr_movf(val2,0)); break;
+ }
+ switch(val1Type)
+ {
+ case num: m_pCode->append(new Instr_sublw(val1.toInt( 0, 0 ))); break;
+ case work: break;
+ case var: m_pCode->append(new Instr_subwf(val1,0)); break;
+ }
+}
+
+void PIC14::assignNum(const QString & val)
+{
+ m_pCode->append(new Instr_movlw(val.toInt( 0, 0 )));
+}
+
+void PIC14::assignVar(const QString &val)
+{
+ m_pCode->append(new Instr_movf(val,0));
+}
+
+void PIC14::saveToReg(const QString &dest)
+{
+ m_pCode->append(new Instr_movwf(dest));
+}
+
+void PIC14::saveResultToVar( const QString & var )
+{
+ m_pCode->append( new Instr_movwf( var ) );
+}
+
+void PIC14::mul(QString val1, QString val2, LocationType val1Type, LocationType val2Type)
+{
+ multiply();
+
+ rearrangeOpArguments( &val1, &val2, &val1Type, &val2Type );
+
+ // First, set _i argument
+ switch(val1Type)
+ {
+ case num: m_pCode->append(new Instr_movlw(val1.toInt( 0, 0 ))); break;
+ case work: break;
+ case var: m_pCode->append(new Instr_movf(val1,0)); break;
+ }
+
+ m_pCode->append(new Instr_movwf("__i"));
+
+ // Then set _j argument
+ switch(val2Type)
+ {
+ case num: m_pCode->append(new Instr_movlw(val2.toInt( 0, 0 ))); break;
+ case work: break;
+ case var: m_pCode->append(new Instr_movf(val2,0)); break;
+ }
+
+ m_pCode->append(new Instr_movwf("__j"));
+ m_pCode->append(new Instr_call("__picfunc_multiply"));
+ m_pCode->append(new Instr_movf("__result",0));
+}
+
+
+void PIC14::multiply()
+{
+ if ( m_pCode->instruction("__picfunc_multiply") )
+ return;
+
+ m_pCode->queueLabel( "__picfunc_multiply", Code::Subroutine );
+ m_pCode->append(new Instr_clrf("__result"), Code::Subroutine ); //result+=m_pCode->appenduction("clrf __result");
+
+ m_pCode->queueLabel( "__picfunc_multiply_loop", Code::Subroutine );
+ m_pCode->append(new Instr_movf("__i",0), Code::Subroutine ); //result+=m_pCode->appenduction("movf __i,0");
+ m_pCode->append(new Instr_btfsc("__j","0"), Code::Subroutine ); //result+=m_pCode->appenduction("btfsc __j,0");
+ m_pCode->append(new Instr_addwf("__result",1), Code::Subroutine ); //result+=m_pCode->appenduction("addwf __result,1");
+ m_pCode->append(new Instr_bcf("STATUS","C"), Code::Subroutine ); //result+=m_pCode->appenduction("bcf STATUS,C");
+ m_pCode->append(new Instr_rrf("__j",1), Code::Subroutine ); //result+=m_pCode->appenduction("rrf __j,1");
+ m_pCode->append(new Instr_bcf("STATUS","C"), Code::Subroutine ); //result+=m_pCode->appenduction("bcf STATUS,C");
+ m_pCode->append(new Instr_rlf("__i",1), Code::Subroutine ); //result+=m_pCode->appenduction("rlf __i,1");
+ m_pCode->append(new Instr_movf("__j",1), Code::Subroutine ); //result+=m_pCode->appenduction("movf __j,1");
+ m_pCode->append(new Instr_btfss("STATUS","Z"), Code::Subroutine ); //result+=m_pCode->appenduction("btfss STATUS,Z");
+ m_pCode->append(new Instr_goto("__picfunc_multiply_loop"), Code::Subroutine ); //result+=m_pCode->appenduction("goto __picfunc_multiply_loop");
+ m_pCode->append(new Instr_return(), Code::Subroutine ); //result+=m_pCode->appenduction("return");
+}
+
+
+void PIC14::div( const QString & val1, const QString & val2, LocationType val1Type, LocationType val2Type)
+{
+ divide();
+
+ // NOO - "x / 2" is NOT the same as "2 / x"
+// rearrangeOpArguments( val1, val2, val1Type, val2Type );
+
+ // First, set _i argument
+ switch(val1Type)
+ {
+ case num: m_pCode->append(new Instr_movlw(val1.toInt( 0, 0 ))); break;
+ case work: break;
+ case var: m_pCode->append(new Instr_movf(val1,0)); break;
+ }
+
+ m_pCode->append(new Instr_movwf("__i"));
+
+ // Then set _j argument
+ switch(val2Type)
+ {
+ case num: m_pCode->append(new Instr_movlw(val2.toInt( 0, 0 ))); break;
+ case work: break;
+ case var: m_pCode->append(new Instr_movf(val2,0)); break;
+ }
+
+ m_pCode->append(new Instr_movwf("__j"));
+
+ m_pCode->append(new Instr_call("__picfunc_divide"));//result+=instruction("call __picfunc_divide");
+ m_pCode->append(new Instr_movf("__result",0));//result+=instruction("movf __result,0");
+}
+
+void PIC14::divide()
+{
+ m_pCode->queueLabel( "__picfunc_divide", Code::Subroutine );
+ m_pCode->append(new Instr_movf("__j",1), Code::Subroutine );
+ m_pCode->append(new Instr_btfsc("STATUS","2"), Code::Subroutine );
+ m_pCode->append(new Instr_return(), Code::Subroutine );
+ m_pCode->append(new Instr_clrf("__result"), Code::Subroutine );
+ m_pCode->append(new Instr_movlw(1), Code::Subroutine );
+ m_pCode->append(new Instr_movwf("__k"), Code::Subroutine );
+
+ m_pCode->queueLabel( "__divide_shift", Code::Subroutine );
+ m_pCode->append(new Instr_bcf("STATUS","C"), Code::Subroutine );
+ m_pCode->append(new Instr_rlf("__k",1), Code::Subroutine );
+ m_pCode->append(new Instr_bcf("STATUS","C"), Code::Subroutine );
+ m_pCode->append(new Instr_rlf("__j",1), Code::Subroutine );
+ m_pCode->append(new Instr_btfss("__j","7"), Code::Subroutine );
+ m_pCode->append(new Instr_goto("__divide_shift"), Code::Subroutine );
+
+ m_pCode->queueLabel( "__divide_loop", Code::Subroutine );
+ m_pCode->append(new Instr_movf("__j",0), Code::Subroutine );
+ m_pCode->append(new Instr_subwf("__i",1), Code::Subroutine );
+ m_pCode->append(new Instr_btfsc("STATUS","C"), Code::Subroutine );
+ m_pCode->append(new Instr_goto("__divide_count"), Code::Subroutine );
+ m_pCode->append(new Instr_addwf("__i",1), Code::Subroutine );
+ m_pCode->append(new Instr_goto("__divide_final"), Code::Subroutine );
+
+ m_pCode->queueLabel( "__divide_count", Code::Subroutine );
+ m_pCode->append(new Instr_movf("__k",0), Code::Subroutine );
+ m_pCode->append(new Instr_addwf("__result",1), Code::Subroutine );
+
+ m_pCode->queueLabel( "__divide_final", Code::Subroutine );
+ m_pCode->append(new Instr_bcf("STATUS","C"), Code::Subroutine );
+ m_pCode->append(new Instr_rrf("__j",1), Code::Subroutine );
+ m_pCode->append(new Instr_bcf("STATUS","C"), Code::Subroutine );
+ m_pCode->append(new Instr_rrf("__k",1), Code::Subroutine );
+ m_pCode->append(new Instr_btfss("STATUS","C"), Code::Subroutine );
+ m_pCode->append(new Instr_goto("__divide_loop"), Code::Subroutine );
+ m_pCode->append(new Instr_return(), Code::Subroutine );
+}
+
+
+Code * PIC14::ifCode()
+{
+ return m_ifCode;
+}
+
+
+Code * PIC14::elseCode()
+{
+ return m_elseCode;
+}
+
+
+void PIC14::ifInitCode( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type )
+{
+ // NOO - "x < 2" is NOT the same as "2 < x"
+// rearrangeOpArguments( val1, val2, val1Type, val2Type );
+
+ switch(val1Type)
+ {
+ case num:
+ m_pCode->append(new Instr_movlw(val1.toInt( 0, 0 )));
+ break;
+
+ case work:
+ break; // Nothing to do
+
+ case var:
+ m_pCode->append(new Instr_movf(val1,0));
+ break;
+ }
+
+ switch(val2Type)
+ {
+ case num:
+ m_pCode->append(new Instr_sublw(val2.toInt( 0, 0 )));
+ break;
+
+ case work:
+ kdError() << k_funcinfo << "Cannot subtract working from working!" << endl;
+ break;
+
+ case var:
+ m_pCode->append(new Instr_subwf(val2,0));
+ break;
+ }
+}
+
+void PIC14::equal( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type )
+{
+ ifInitCode( val1, val2, val1Type, val2Type );
+ const QString labelEnd = mb->uniqueLabel()+"_endif";
+ const QString labelFalse = mb->uniqueLabel()+"_case_false";
+
+ m_pCode->append(new Instr_btfss("STATUS","2"));
+ m_pCode->append(new Instr_goto(labelFalse));
+
+ mergeCode( ifCode() );
+
+ m_pCode->append(new Instr_goto(labelEnd));
+
+ m_pCode->queueLabel( labelFalse );
+ mergeCode( elseCode() );
+ m_pCode->queueLabel( labelEnd );
+}
+
+void PIC14::notEqual( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type )
+{
+ ifInitCode( val1, val2, val1Type, val2Type );
+ const QString labelEnd = mb->uniqueLabel()+"_endif";
+ const QString labelFalse = mb->uniqueLabel()+"_case_false";
+
+ m_pCode->append(new Instr_btfsc("STATUS","2"));
+ m_pCode->append(new Instr_goto(labelFalse));
+
+ mergeCode( ifCode() );
+
+ m_pCode->append(new Instr_goto(labelEnd));
+
+ m_pCode->queueLabel( labelFalse );
+ mergeCode( elseCode() );
+ m_pCode->queueLabel( labelEnd );
+}
+
+void PIC14::greaterThan( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type )
+{
+ ifInitCode( val1, val2, val1Type, val2Type );
+ const QString labelEnd = mb->uniqueLabel()+"_endif";
+ const QString labelFalse = mb->uniqueLabel()+"_case_false";
+
+ m_pCode->append(new Instr_btfsc("STATUS","0"));
+ m_pCode->append(new Instr_goto(labelFalse));
+
+ mergeCode( ifCode() );
+ m_pCode->append(new Instr_goto(labelEnd));
+
+ m_pCode->queueLabel( labelFalse );
+ mergeCode( elseCode() );
+ m_pCode->queueLabel( labelEnd );
+}
+
+void PIC14::lessThan( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type )
+{
+ cout << k_funcinfo << endl;
+ ifInitCode( val1, val2, val1Type, val2Type );
+ const QString labelEnd = mb->uniqueLabel()+"_endif";
+ const QString labelFalse = mb->uniqueLabel()+"_case_false";
+
+ m_pCode->append(new Instr_btfss("STATUS","0"));
+ m_pCode->append(new Instr_goto(labelFalse));
+ m_pCode->append(new Instr_btfsc("STATUS","2"));
+ m_pCode->append(new Instr_goto(labelFalse));
+
+ mergeCode( ifCode() );
+
+ m_pCode->append(new Instr_goto(labelEnd));
+
+ m_pCode->queueLabel( labelFalse );
+ mergeCode( elseCode() );
+ m_pCode->queueLabel( labelEnd );
+}
+
+void PIC14::greaterOrEqual( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type )
+{
+ ifInitCode( val1, val2, val1Type, val2Type );
+ const QString labelEnd = mb->uniqueLabel()+"_endif";
+ const QString labelTrue = mb->uniqueLabel()+"_case_true"; // Note that unlike the others, this is labelTrue, not labelFalse
+
+ m_pCode->append(new Instr_btfsc("STATUS","2"));
+ m_pCode->append(new Instr_goto(labelTrue));
+ m_pCode->append(new Instr_btfss("STATUS","0"));
+ m_pCode->append(new Instr_goto(labelTrue));
+
+ mergeCode( elseCode() );
+
+ m_pCode->append(new Instr_goto(labelEnd));
+
+ m_pCode->queueLabel( labelTrue );
+ mergeCode( ifCode() );
+ m_pCode->queueLabel( labelEnd );
+}
+
+void PIC14::lessOrEqual( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type )
+{
+ ifInitCode( val1, val2, val1Type, val2Type );
+ const QString labelEnd = mb->uniqueLabel()+"_endif";
+ const QString labelFalse = mb->uniqueLabel()+"_case_false";
+
+ m_pCode->append(new Instr_btfss("STATUS","0"));
+ m_pCode->append(new Instr_goto(labelFalse));
+
+ mergeCode( ifCode() );
+ m_pCode->append(new Instr_goto(labelEnd));
+
+ m_pCode->queueLabel( labelFalse );
+ mergeCode( elseCode() );
+ m_pCode->queueLabel( labelEnd );
+}
+
+
+void PIC14::Swhile( Code * whileCode, const QString &expression)
+{
+ QString result;
+ QString ul = mb->uniqueLabel();
+
+ whileCode->append( new Instr_goto(ul) );
+
+ m_pCode->queueLabel( ul, Code::Middle );
+
+ // If the condition is not true, just fall through
+ m_parser->compileConditionalExpression( expression, whileCode, 0 );
+}
+
+
+void PIC14::Srepeat( Code * repeatCode, const QString &expression)
+{
+ QString result;
+ QString ul = mb->uniqueLabel();
+
+ Code * elseCode = new Code;
+ elseCode->append( new Instr_goto(ul) );
+
+ m_pCode->queueLabel( ul );
+ m_pCode->merge( repeatCode );
+
+ // If the condition is true, just fall through
+ m_parser->compileConditionalExpression( expression, 0, elseCode );
+}
+
+void PIC14::Sif( Code * ifCode, Code * elseCode, const QString &expression)
+{
+ m_parser->compileConditionalExpression( expression, ifCode, elseCode );
+}
+
+
+void PIC14::Sfor( Code * forCode, Code * initCode, const QString &expression, const QString &variable, const QString &step, bool stepPositive)
+{
+ QString ul = mb->uniqueLabel();
+
+ if ( step == "1" )
+ {
+ if (stepPositive)
+ forCode->append(new Instr_incf(variable,1));
+ else
+ forCode->append(new Instr_decf(variable,1));
+ }
+ else
+ {
+ forCode->append(new Instr_movlw(step.toInt( 0, 0 )));
+ if (stepPositive)
+ forCode->append(new Instr_addwf(variable,1));
+ else
+ forCode->append(new Instr_subwf(variable,1));
+ }
+ forCode->append(new Instr_goto(ul));
+
+ m_pCode->merge( initCode );
+
+ m_pCode->queueLabel( ul );
+
+ m_parser->compileConditionalExpression( expression, forCode, 0 );
+}
+
+
+void PIC14::Spin( const PortPin & portPin, bool NOT)
+{
+ QString lowLabel, highLabel, postLabel;
+ lowLabel = mb->uniqueLabel();
+ highLabel = mb->uniqueLabel();
+ postLabel = mb->uniqueLabel();
+ /*result += indent + "goto\t" + lowLabel;
+ result += indent + "movlw\t1" + "goto\t"+postLabel+;
+ result += lowLabel + + indent + "movlw\t0" + indent;
+ result += postLabel + ;*/
+
+ if(NOT)
+ m_pCode->append(new Instr_btfsc( portPin.port(), QString::number( portPin.pin() ) ));
+ //result +=instruction((QString)(NOT?"btfsc":"btfss")+"\t"+port+","+pin);
+ else
+ m_pCode->append(new Instr_btfss( portPin.port(), QString::number( portPin.pin() ) ));
+
+ m_pCode->append(new Instr_goto(lowLabel));//result += instruction("goto\t" + lowLabel);
+ mergeCode( ifCode() );
+ m_pCode->append(new Instr_goto(postLabel));//result += instruction("goto\t"+postLabel);
+
+ m_pCode->queueLabel( lowLabel );
+ mergeCode( elseCode() );
+
+ m_pCode->queueLabel( postLabel );
+}
+
+
+void PIC14::Sdelay( unsigned length_us, Code::InstructionPosition pos )
+{
+ if ( length_us == 0 )
+ return;
+
+ if ( length_us > 50070524 )
+ {
+ length_us += 50267642;
+ int l = length_us/50070530;
+ length_us -= l * 50070530;
+ int k = length_us/196355;
+
+ m_pCode->append( new Instr_movlw( l ), pos );
+ m_pCode->append( new Instr_movwf( "__l" ), pos );
+ m_pCode->append( new Instr_movlw( k ), pos );
+ m_pCode->append( new Instr_movwf( "__k" ), pos );
+
+ mb->addDelayRoutineWanted( Delay_50S );
+ }
+
+ else if ( length_us > 196350 )
+ {
+ length_us += 197116;
+ int k = length_us/196355;
+ length_us -= k * 196355;
+ int j = length_us/770;
+
+ m_pCode->append( new Instr_incf( "__l", 1 ), pos );
+ m_pCode->append( new Instr_movlw( k ), pos );
+ m_pCode->append( new Instr_movwf( "__k" ), pos );
+ m_pCode->append( new Instr_movlw( j ), pos );
+ m_pCode->append( new Instr_movwf( "__j" ), pos );
+
+ mb->addDelayRoutineWanted( Delay_200mS );
+ }
+
+ else if ( length_us > 766 )
+ {
+ length_us += 765;
+ int j = length_us/770;
+ length_us -= j * 770;
+ int i = length_us/3;
+
+ m_pCode->append( new Instr_incf( "__l", 1 ), pos );
+ m_pCode->append( new Instr_incf( "__k", 1 ), pos );
+ m_pCode->append( new Instr_movlw( j ), pos );
+ m_pCode->append( new Instr_movwf( "__j" ), pos );
+ m_pCode->append( new Instr_movlw( i ), pos );
+ m_pCode->append( new Instr_movwf( "__i" ), pos );
+
+ mb->addDelayRoutineWanted( Delay_768uS );
+ }
+
+ else
+ {
+ length_us += -1;
+ int i = length_us/3;
+
+ m_pCode->append( new Instr_incf( "__l", 1 ), pos );
+ m_pCode->append( new Instr_incf( "__k", 1 ), pos );
+ m_pCode->append( new Instr_incf( "__j", 1 ), pos );
+ m_pCode->append( new Instr_movlw( i ), pos );
+ m_pCode->append( new Instr_movwf( "__i" ), pos );
+
+ mb->addDelayRoutineWanted( Delay_3uS );
+ }
+
+ m_pCode->append( new Instr_call( "__delay_subroutine"), pos );
+}
+
+
+void PIC14::addCommonFunctions( DelaySubroutine delay )
+{
+ if ( delay != Delay_None )
+ {
+ QString subName = "__delay_subroutine";
+ m_pCode->queueLabel( subName, Code::Subroutine );
+
+ m_pCode->append( new Instr_decfsz( "__i", 1 ), Code::Subroutine );
+ m_pCode->append( new Instr_goto( subName ), Code::Subroutine );
+
+ if ( delay > Delay_3uS )
+ {
+ m_pCode->append( new Instr_decfsz( "__j", 1 ), Code::Subroutine );
+ m_pCode->append( new Instr_goto( subName ), Code::Subroutine );
+ }
+
+ if ( delay > Delay_768uS )
+ {
+ m_pCode->append( new Instr_decfsz( "__k", 1 ), Code::Subroutine );
+ m_pCode->append( new Instr_goto( subName ), Code::Subroutine );
+ }
+
+ if ( delay > Delay_200mS )
+ {
+ m_pCode->append( new Instr_decfsz( "__l", 1 ), Code::Subroutine );
+ m_pCode->append( new Instr_goto( subName ), Code::Subroutine );
+ }
+
+ m_pCode->append( new Instr_return(), Code::Subroutine );
+ }
+}
+
+
+void PIC14::SsevenSegment( const Variable & pinMap )
+{
+ assert( pinMap.type() == Variable::sevenSegmentType );
+ assert( pinMap.portPinList().size() == 7 );
+
+ QString subName = QString("__output_seven_segment_%1").arg( pinMap.name() );
+
+ m_pCode->append( new Instr_call( subName ) );
+
+ if ( m_pCode->instruction(subName) )
+ return;
+
+ // Build up what are going to write to each port from the pin map
+ struct SSPortOutput
+ {
+ bool used; // Wheter we use this port at all
+ bool use[8]; // Whether or not we use each pin.
+ bool out[16][8]; // The bit to write to each pin for each value.
+ uchar useMask; // The bits of use[8] - this is generated later from use[8]
+ };
+
+ unsigned numPorts = 2;
+ SSPortOutput portOutput[ numPorts ];
+ memset( portOutput, 0, numPorts * sizeof(SSPortOutput) );
+
+ for ( unsigned i = 0; i < 7; ++i )
+ {
+ PortPin portPin = pinMap.portPinList()[i];
+
+ unsigned port = unsigned( portPin.portPosition() );
+ unsigned pin = unsigned( portPin.pin() );
+
+ portOutput[ port ].used = true;
+ portOutput[ port ].use[ pin ] = true;
+
+ for ( unsigned num = 0; num < 16; ++num )
+ {
+ portOutput[ port ].out[ num ][ pin ] = LEDSegTable[num][ i ];
+ }
+ }
+
+
+ // See if we've used more than one port
+ unsigned portsUsed = 0;
+ for ( unsigned port = 0; port < numPorts; ++port )
+ {
+ if ( portOutput[port].used )
+ portsUsed++;
+ }
+
+
+ // Generate the useMasks
+ for ( unsigned port = 0; port < numPorts; ++port )
+ {
+ portOutput[port].useMask = 0;
+ for ( unsigned pin = 0; pin < 8; ++pin )
+ portOutput[port].useMask |= portOutput[port].use[pin] ? (1 << pin) : 0;
+ }
+
+
+ //BEGIN Generate [subName] Subroutine
+ m_pCode->queueLabel( subName, Code::Subroutine );
+// if ( portsUsed > 1 )
+ {
+ m_pCode->append( new Instr_movwf("__i"), Code::Subroutine );
+ }
+
+// bool overwrittenW = false;
+ bool overwrittenW = true;
+
+ for ( unsigned port = 0; port < numPorts; ++port )
+ {
+ if ( !portOutput[port].used )
+ continue;
+
+ QString portName = QString("PORT%1").arg( char('A'+port) );
+
+ // Save the current value of the port pins that we should not be writing to
+ m_pCode->append( new Instr_movf( portName, 0 ), Code::Subroutine );
+ m_pCode->append( new Instr_andlw( ~portOutput[port].useMask ), Code::Subroutine );
+ m_pCode->append( new Instr_movwf( "__j" ), Code::Subroutine );
+
+ if ( overwrittenW )
+ m_pCode->append( new Instr_movf("__i",0), Code::Subroutine );
+
+ m_pCode->append( new Instr_call( subName + QString("_lookup_%1").arg(port) ), Code::Subroutine );
+ overwrittenW = true;
+
+ // Restore the state of the pins which aren't used
+ m_pCode->append( new Instr_iorwf( "__j", 0 ), Code::Subroutine );
+
+ // And write the result to the port
+ m_pCode->append( new Instr_movwf( portName ), Code::Subroutine );
+ }
+
+ m_pCode->append( new Instr_return(), Code::Subroutine );
+ //END Generate [subName] Subroutine
+
+ // For each port, generate code for looking up the value for writing to it
+ for ( unsigned port = 0; port < numPorts; ++port )
+ {
+ if ( !portOutput[port].used )
+ continue;
+
+ m_pCode->queueLabel( subName + QString("_lookup_%1").arg(port), Code::LookupTable );
+ m_pCode->append( new Instr_andlw(15), Code::LookupTable );
+
+ // Generate the lookup table
+ m_pCode->append( new Instr_addwf( "pcl", 1 ), Code::LookupTable );
+ for ( unsigned num = 0; num < 16; ++num )
+ {
+ unsigned literal = 0;
+ for ( unsigned bit = 0; bit < 8; ++bit )
+ literal += ( portOutput[port].out[num][bit] ? 1 : 0 ) << bit;
+
+ m_pCode->append( new Instr_retlw( literal ), Code::LookupTable );
+ }
+ }
+}
+
+
+void PIC14::Skeypad( const Variable & pinMap )
+{
+ // pinMap = 4 rows, n columns
+
+ assert( pinMap.type() == Variable::keypadType );
+ assert( pinMap.portPinList().size() >= 7 ); // 4 rows, at least 3 columns
+
+ QString subName = QString("__wait_read_keypad_%1").arg( pinMap.name() );
+ QString waitName = QString("__wait_keypad_%1").arg( pinMap.name() );
+ QString readName = QString("__read_keypad_%1").arg( pinMap.name() );
+
+ m_pCode->append( new Instr_call( subName ) );
+
+ if ( m_pCode->instruction( subName ) )
+ return;
+
+ //BEGIN Wait until read subroutine
+ m_pCode->queueLabel( subName, Code::Subroutine );
+
+ // Read current key (if any) from keypad and save to temporary variable
+ m_pCode->append( new Instr_call( readName ), Code::Subroutine );
+ m_pCode->append( new Instr_movwf( "__m" ), Code::Subroutine );
+
+ // Test if any key was pressed; if not, then start again
+// std::cout << "mb->alias(\"Keypad_None\")="<<mb->alias("Keypad_None") << std::endl;
+ m_pCode->append( new Instr_sublw( mb->alias("Keypad_None").toInt( 0, 0 ) ), Code::Subroutine );
+ m_pCode->append( new Instr_btfsc( "STATUS","Z" ), Code::Subroutine );
+ m_pCode->append( new Instr_goto( subName ), Code::Subroutine );
+ m_pCode->append( new Instr_goto( waitName ), Code::Subroutine );
+ //END Wait until read subroutine
+
+
+ //BEGIN Wait until released subroutine
+ m_pCode->queueLabel( waitName, Code::Subroutine );
+
+ Sdelay( 10000, Code::Subroutine ); // 10 milliseconds for debouncing
+
+ // Key was pressed; now we wait until the key is released again
+ m_pCode->append( new Instr_call( readName ), Code::Subroutine );
+ m_pCode->append( new Instr_sublw( mb->alias("Keypad_None").toInt( 0, 0 ) ), Code::Subroutine );
+ m_pCode->append( new Instr_btfss( "STATUS","Z" ), Code::Subroutine );
+ m_pCode->append( new Instr_goto( waitName ), Code::Subroutine );
+ m_pCode->append( new Instr_movf( "__m", 0 ), Code::Subroutine );
+ m_pCode->append( new Instr_return(), Code::Subroutine );
+ //END Wait until released subroutine
+
+
+ if ( m_pCode->instruction( readName ) )
+ return;
+
+ //BEGIN Read current value of keypad subroutine
+ m_pCode->queueLabel( readName, Code::Subroutine );
+
+ // Make the four row lines low
+ for ( unsigned row = 0; row < 4; ++ row )
+ {
+ PortPin rowPin = pinMap.portPinList()[row];
+ m_pCode->append( new Instr_bcf( rowPin.port(), QString::number( rowPin.pin() ) ), Code::Subroutine );
+ }
+
+ // Test each row in turn
+ for ( unsigned row = 0; row < 4; ++ row )
+ {
+ // Make the high low
+ PortPin rowPin = pinMap.portPinList()[row];
+ m_pCode->append( new Instr_bsf( rowPin.port(), QString::number( rowPin.pin() ) ), Code::Subroutine );
+
+ for ( unsigned col = 0; col < 3; ++ col )
+ {
+ PortPin colPin = pinMap.portPinList()[4+col];
+ m_pCode->append( new Instr_btfsc( colPin.port(), QString::number( colPin.pin() ) ), Code::Subroutine );
+ m_pCode->append( new Instr_retlw( mb->alias( QString("Keypad_%1_%2").arg(row+1).arg(col+1) ).toInt( 0, 0 ) ), Code::Subroutine );
+ }
+
+ // Make the low again
+ m_pCode->append( new Instr_bcf( rowPin.port(), QString::number( rowPin.pin() ) ), Code::Subroutine );
+ }
+
+ // No key was pressed
+ m_pCode->append( new Instr_retlw( mb->alias("Keypad_None").toInt( 0, 0 ) ), Code::Subroutine );
+ //END Read current value of keypad subroutine
+}
+
+
+void PIC14::bitwise( Expression::Operation op, const QString &r_val1, const QString &val2, bool val1IsNum, bool val2IsNum )
+{
+ QString val1 = r_val1;
+ // There is no instruction for notting a literal,
+ // so instead I am going to XOR with 0xFF
+ if( op == Expression::bwnot ) val1 = "0xFF";
+ if( val1IsNum ) m_pCode->append(new Instr_movlw(val1.toInt( 0, 0 )));// result += instruction("movlw\t"+val1);
+ else m_pCode->append(new Instr_movf(val1,0));//result += instruction("movf\t"+val1+",0");
+
+ QString opString;
+ if( val2IsNum )
+ {
+ switch(op)
+ {
+ case Expression::bwand: m_pCode->append(new Instr_andlw(val2.toInt( 0, 0 ))); break;
+ case Expression::bwor: m_pCode->append(new Instr_iorlw(val2.toInt( 0, 0 ))); break;
+ case Expression::bwxor: m_pCode->append(new Instr_xorlw(val2.toInt( 0, 0 ))); break;
+ case Expression::bwnot: m_pCode->append(new Instr_xorlw(val2.toInt( 0, 0 ))); break;
+ default: break;
+ }
+ }
+ else
+ {
+ switch(op)
+ {
+ case Expression::bwand: m_pCode->append(new Instr_andwf(val2,0)); break;
+ case Expression::bwor: m_pCode->append(new Instr_iorwf(val2,0)); break;
+ case Expression::bwxor: m_pCode->append(new Instr_xorwf(val2,0)); break;
+ case Expression::bwnot: m_pCode->append(new Instr_xorwf(val2,0)); break;
+ default: break;
+ }
+
+ }
+}
+
+void PIC14::SincVar( const QString &var )
+{
+ m_pCode->append(new Instr_incf(var,1) );
+}
+
+void PIC14::SdecVar( const QString &var )
+{
+ m_pCode->append(new Instr_decf(var,1) );
+}
+
+void PIC14::SrotlVar( const QString &var )
+{
+ m_pCode->append(new Instr_rlf(var,1));
+}
+
+void PIC14::SrotrVar( const QString &var )
+{
+ m_pCode->append(new Instr_rrf(var,1));
+}
+
+void PIC14::Stristate(const QString &port)
+{
+ m_pCode->append( new Instr_bsf("STATUS","5") );
+
+ if( port == "trisa" || port == "TRISA" )
+ saveResultToVar( "TRISA" );
+ else
+ saveResultToVar( "TRISB" );
+
+ m_pCode->append( new Instr_bcf(Register("STATUS"),"5") );
+}
+
+void PIC14::Sasm(const QString &raw)
+{
+ m_pCode->append(new Instr_asm(raw));
+}
+
+
+
+
+//BEGIN class PortPin
+PortPin::PortPin( const QString & port, int pin )
+{
+ m_port = port.upper();
+ m_pin = pin;
+}
+
+
+PortPin::PortPin()
+{
+ m_port = ' ';
+ m_pin = -1;
+}
+
+
+int PortPin::portPosition() const
+{
+ if ( m_port.isEmpty() )
+ return 0;
+ return uchar( m_port[ m_port.length() - 1 ] ) - 'A';
+}
+//END class PortPin
diff --git a/microbe/pic14.h b/microbe/pic14.h
new file mode 100644
index 0000000..5fd5a40
--- /dev/null
+++ b/microbe/pic14.h
@@ -0,0 +1,253 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef PIC14_H
+#define PIC14_H
+
+#include "expression.h"
+#include "microbe.h"
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qvaluelist.h>
+
+class Code;
+class Microbe;
+class Parser;
+
+/**
+@author David Saxton
+ */
+class PortPin
+{
+ public:
+ PortPin( const QString & port, int pin );
+ /**
+ * Creates an invalid PortPin ( pin() will return -1).
+ */
+ PortPin();
+ /**
+ * Returns port (uppercase).
+ */
+ QString port() const { return m_port; }
+ /**
+ * Returns the port position (e.g. "PORTA" is 0, "PORTB" is 1, etc).
+ */
+ int portPosition() const;
+ /**
+ * Returns the pin (-1 == invalid PortPin).
+ */
+ int pin() const { return m_pin; }
+
+ protected:
+ QString m_port;
+ int m_pin;
+};
+typedef QValueList<PortPin> PortPinList;
+
+
+/**
+@author Daniel Clarke
+@author David Saxton
+*/
+class PIC14
+{
+ public:
+ enum Type
+ {
+ P16C84,
+ P16F84,
+ P16F627,
+ P16F628,
+ unknown,
+ };
+ enum LocationType
+ {
+ num = 1,
+ work = 2,
+ var = 3,
+ };
+ /**
+ * Used in determining which delay subroutine should be created.
+ */
+ enum DelaySubroutine
+ {
+ Delay_None = 0,
+ Delay_3uS = 1,
+ Delay_768uS = 2,
+ Delay_200mS = 3,
+ Delay_50S = 4,
+ };
+
+ PIC14( Microbe * master, Type type );
+ ~PIC14();
+
+ /**
+ * Tries to convert the string to a PIC type, returning unknown if
+ * unsuccessful.
+ */
+ static Type toType( const QString & text );
+ /**
+ * @return the PIC type.
+ */
+ Type type() const { return m_type; }
+ /**
+ * @return the Type as a string without the P at the front.
+ */
+ QString minimalTypeString() const;
+ /**
+ * Translates the portPinString (e.g. "PORTA.2") into a PortPin if the port
+ * and pin combination is valid (otherwise returns an invalid PortPin with
+ * a pin of -1.
+ */
+ PortPin toPortPin( const QString & portPinString );
+ /**
+ * Returns the address that the General Purpose Registers starts at.
+ */
+ uchar gprStart() const;
+
+ void setParser(Parser *parser) { m_parser = parser; }
+ void setCode( Code * code ) { m_pCode = code; }
+ void mergeCode( Code * code );
+
+ void setConditionalCode( Code * ifCode, Code * elseCode );
+ Code * ifCode();
+ Code * elseCode();
+
+ Code * m_ifCode;
+ Code * m_elseCode;
+
+ void postCompileConstruct( const QStringList &interrupts );
+
+ /**
+ * @returns whether or not the port is valid.
+ * @see isValidPortPin
+ */
+ bool isValidPort( const QString & portName ) const;
+ /**
+ * @returns whether or not the port and pin is a valid combination.
+ * @see isValidPort
+ */
+ bool isValidPortPin( const PortPin & portPin ) const;
+ bool isValidTris( const QString & trisName ) const;
+ bool isValidInterrupt( const QString & interruptName ) const;
+
+ void Sgoto(const QString &label);
+ void Slabel(const QString &label);
+ void Send();
+ void Ssubroutine(const QString &procName, Code * compiledProcCode);
+ void Sinterrupt(const QString & procName, Code * compiledProcCode);
+ void Scall(const QString &name);
+
+ void Ssetlh( const PortPin & portPin, bool high);
+
+ void add( QString val1, QString val2, LocationType val1Type, LocationType val2Type );
+ void subtract( const QString & val1, const QString & val2, LocationType val1Type, LocationType val2Type );
+ void mul( QString val1, QString val2, LocationType val1Type, LocationType val2Type);
+ void div( const QString & val1, const QString & val2, LocationType val1Type, LocationType val2Type);
+
+ void assignNum(const QString & val);
+ void assignVar(const QString & val);
+
+ void saveToReg(const QString &dest);
+ /**
+ * Move the contents of the working register to the register with the given
+ * name.
+ */
+ void saveResultToVar( const QString & var );
+ /** Code for "==" */
+ void equal( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type );
+ /** Code for "!=" */
+ void notEqual( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type );
+ /** Code for ">" */
+ void greaterThan( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type );
+ /** Code for "<" */
+ void lessThan( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type );
+ /** Code for ">=" */
+ void greaterOrEqual( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type );
+ /** Code for "<=" */
+ void lessOrEqual( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type );
+
+ void bitwise( Expression::Operation op, const QString &val1, const QString &val2, bool val1IsNum, bool val2IsNum );
+
+ void Swhile( Code * whileCode, const QString &expression);
+ void Srepeat( Code * repeatCode, const QString &expression);
+ void Sif( Code * ifCode, Code * elseCode, const QString &expression);
+ void Sfor( Code * forCode, Code * initCode, const QString &expression, const QString &variable, const QString &step, bool stepPositive);
+
+ void Spin( const PortPin & portPin, bool NOT);
+ void addCommonFunctions( DelaySubroutine delay );
+
+ //BEGIN "Special Functionality" functions
+ /**
+ * Delay the program execution, for the given period of length_us (unit:
+ * microseconds).
+ * @param pos the position to add the code for calling the delay subroutine.
+ */
+ void Sdelay( unsigned length_us, Code::InstructionPosition pos = Code::Middle );
+ /**
+ * Output the working register to the given seven segment.
+ * @param name The variable giving the pin configuration of the seven
+ * segment.
+ */
+ void SsevenSegment( const Variable & pinMap );
+ /**
+ * Read the value of the keypad to the working register.
+ * @param name The variable giving the pin configuration of the keypad.
+ */
+ void Skeypad( const Variable & pinMap );
+ //END "Special Functionality" functions
+
+ void SincVar( const QString &var );
+ void SdecVar( const QString &var );
+ void SrotlVar( const QString &var );
+ void SrotrVar( const QString &var );
+
+ void Stristate( const QString &port );
+
+ void Sasm(const QString &raw);
+
+ protected:
+ void multiply();
+ void divide();
+
+ /** @see Microbe::m_picType */
+ Type m_type;
+
+ Parser * m_parser;
+ Microbe * mb;
+ Code * m_pCode;
+
+ void ifInitCode( const QString &val1, const QString &val2, LocationType val1Type, LocationType val2Type );
+
+ /**
+ * The function makes sure that val1 always contains a working register
+ * variable, if one has been passed, this is done by swapping val1 and val2 when
+ * neccessary
+ */
+ void rearrangeOpArguments( QString * val1, QString * val2, LocationType * val1Type, LocationType * val2Type);
+
+ /**
+ * @param flag True means give flag bit, false means give enable bit instead
+ */
+ int interruptNameToBit(const QString &name, bool flag);
+};
+
+#endif
diff --git a/microbe/traverser.cpp b/microbe/traverser.cpp
new file mode 100644
index 0000000..e066381
--- /dev/null
+++ b/microbe/traverser.cpp
@@ -0,0 +1,100 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "traverser.h"
+#include "pic14.h"
+
+Traverser::Traverser(BTreeNode *root)
+{
+ m_root = root;
+ m_current = root;
+}
+
+Traverser::~Traverser()
+{
+}
+
+BTreeNode * Traverser::start()
+{
+ /* To find the start we will iterate, or possibly recurse
+ down the tree, each time turning down the node that has children,
+ if they both have no children we have reached the end and it shouldn't
+ really matter which we pick (check this algorithm) */
+
+ BTreeNode *n = m_root;
+ bool found = false;
+
+ while(!found)
+ {
+ if( !n->hasChildren() ) found = true;
+ else
+ {
+ if( !n->left()->hasChildren() )
+ {
+ if( !n->right()->hasChildren() ) found = true;
+ n = n->right();
+ }
+ else n = n->left();
+ }
+ }
+ //if(n->parent()) m_current = n->parent();
+ //else m_current = n;
+ m_current = n;
+ return m_current;
+}
+
+BTreeNode * Traverser::next()
+{
+ // a.t.m we will just take the next thing to be the parent.
+ if( m_current != m_root ) m_current = m_current->parent();
+ return m_current;
+}
+
+bool Traverser::onLeftBranch()
+{
+ return current()->parent()->left() == current();
+}
+
+BTreeNode * Traverser::oppositeNode()
+{
+ if ( onLeftBranch() )
+ return current()->parent()->right();
+ else
+ return current()->parent()->left();
+}
+
+void Traverser::descendLeftwardToTerminal()
+{
+ bool done = false;
+ while(!done)
+ {
+ if( !current()->hasChildren() ) return;
+ else
+ {
+ m_current = current()->left();
+ }
+ }
+}
+
+void Traverser::moveToParent()
+{
+ if(current()->parent()) m_current = current()->parent();
+}
+
diff --git a/microbe/traverser.h b/microbe/traverser.h
new file mode 100644
index 0000000..7593ba7
--- /dev/null
+++ b/microbe/traverser.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke *
+ * daniel.jc@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef TRAVERSER_H
+#define TRAVERSER_H
+
+#include "btreenode.h"
+
+/**
+Keeps persistant information needed and the algorithm for traversing the binary trees made of BTreeNodes, initialise either by passing a BTreeBase or BTreeNode to traverse a sub tree.
+
+Note that this is designed for traversing in the *reverse* way starting at the end of each branch
+in order to calculate the expression contained in the tree.
+
+@author Daniel Clarke
+*/
+class Traverser
+{
+public:
+ Traverser(BTreeNode *root);
+ ~Traverser();
+
+ /** Find where to start in the tree and return it also resets all the data related to the traversal. */
+ BTreeNode *start();
+
+ /** Finds the next node to move to and returns it. */
+ BTreeNode *next();
+
+ /** Returns true if we are on the left branch, false otherwise. */
+ bool onLeftBranch();
+
+ /** Returns the node on the opposite branch of the parent. */
+ BTreeNode * oppositeNode();
+
+ BTreeNode * current() const { return m_current; }
+
+ void setCurrent(BTreeNode *current){m_current = current;}
+
+ /** From the current position, go down the tree taking a left turn always,
+ and stopping when reaching the left terminal node.
+ */
+ void descendLeftwardToTerminal();
+
+ /** It might occur in the future that next() does not just move to the parent,
+ so use this for moving to parent
+ */
+ void moveToParent();
+
+ BTreeNode *root() const {return m_root;}
+
+protected:
+ BTreeNode *m_root;
+ BTreeNode *m_current;
+};
+
+#endif
diff --git a/microbe/variable.cpp b/microbe/variable.cpp
new file mode 100644
index 0000000..dbc20ba
--- /dev/null
+++ b/microbe/variable.cpp
@@ -0,0 +1,79 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke daniel.jc@gmail.com *
+ * Copyright (C) 2005 by David Saxton *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "pic14.h"
+#include "variable.h"
+
+Variable::Variable( VariableType type, const QString & name )
+{
+ m_type = type;
+ m_name = name;
+}
+
+
+Variable::Variable()
+{
+ m_type = invalidType;
+}
+
+
+Variable::~Variable()
+{
+}
+
+
+void Variable::setPortPinList( const PortPinList & portPinList )
+{
+ m_portPinList = portPinList;
+}
+
+
+bool Variable::isReadable() const
+{
+ switch (m_type)
+ {
+ case charType:
+ case keypadType:
+ return true;
+ case sevenSegmentType:
+ case invalidType:
+ return false;
+ }
+
+ return false;
+}
+
+
+bool Variable::isWritable() const
+{
+ switch (m_type)
+ {
+ case charType:
+ case sevenSegmentType:
+ return true;
+ case keypadType:
+ case invalidType:
+ return false;
+ }
+
+ return false;
+}
+
+
diff --git a/microbe/variable.h b/microbe/variable.h
new file mode 100644
index 0000000..f5848c1
--- /dev/null
+++ b/microbe/variable.h
@@ -0,0 +1,79 @@
+/***************************************************************************
+ * Copyright (C) 2004-2005 by Daniel Clarke daniel.jc@gmail.com *
+ * Copyright (C) 2005 by David Saxton *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef VARIABLE_H
+#define VARIABLE_H
+
+#include <qstring.h>
+#include <qvaluelist.h>
+
+class PortPin;
+typedef QValueList<PortPin> PortPinList;
+
+
+/**
+@author Daniel Clarke
+@author David Saxton
+*/
+class Variable
+{
+ public:
+ enum VariableType
+ {
+ charType, // 8 bit file register
+ sevenSegmentType, // A pin configuration for a seven segment is represented by a write-only variable.
+ keypadType, // A pin configuration for a keypad has 4 rows and n columns (typically n = 3 or 4) - a read-only variable
+ invalidType
+ };
+
+ Variable( VariableType type, const QString & name );
+ Variable();
+ ~Variable();
+
+ VariableType type() const { return m_type; }
+ QString name() const { return m_name; }
+
+ /**
+ * @returns whether the variable can be read from (e.g. the seven
+ * segment variable cannot).
+ */
+ bool isReadable() const;
+ /**
+ * @returns whether the variable can be written to (e.g. the keypad
+ * variable cannot).
+ */
+ bool isWritable() const;
+ /**
+ * @see portPinList
+ */
+ void setPortPinList( const PortPinList & portPinList );
+ /**
+ * Used in seven-segments and keypads,
+ */
+ PortPinList portPinList() const { return m_portPinList; }
+
+ protected:
+ VariableType m_type;
+ QString m_name;
+ PortPinList m_portPinList;
+};
+typedef QValueList<Variable> VariableList;
+
+#endif