From 5de3dd4762ca33a0f92e79ffa4fe2ff67069d531 Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 24 Feb 2010 01:49:02 +0000 Subject: Added KDE3 version of ktechlab git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/ktechlab@1095338 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- microbe/Makefile.am | 10 + microbe/btreebase.cpp | 248 +++++ microbe/btreebase.h | 59 ++ microbe/btreenode.cpp | 66 ++ microbe/btreenode.h | 107 +++ microbe/expression.cpp | 842 +++++++++++++++++ microbe/expression.h | 126 +++ microbe/instruction.cpp | 2309 +++++++++++++++++++++++++++++++++++++++++++++++ microbe/instruction.h | 1273 ++++++++++++++++++++++++++ microbe/main.cpp | 80 ++ microbe/microbe.cpp | 472 ++++++++++ microbe/microbe.h | 249 +++++ microbe/optimizer.cpp | 512 +++++++++++ microbe/optimizer.h | 87 ++ microbe/parser.cpp | 1054 +++++++++++++++++++++ microbe/parser.h | 293 ++++++ microbe/pic14.cpp | 1196 ++++++++++++++++++++++++ microbe/pic14.h | 253 ++++++ microbe/traverser.cpp | 100 ++ microbe/traverser.h | 72 ++ microbe/variable.cpp | 79 ++ microbe/variable.h | 79 ++ 22 files changed, 9566 insertions(+) create mode 100644 microbe/Makefile.am create mode 100644 microbe/btreebase.cpp create mode 100644 microbe/btreebase.h create mode 100644 microbe/btreenode.cpp create mode 100644 microbe/btreenode.h create mode 100644 microbe/expression.cpp create mode 100644 microbe/expression.h create mode 100644 microbe/instruction.cpp create mode 100644 microbe/instruction.h create mode 100644 microbe/main.cpp create mode 100644 microbe/microbe.cpp create mode 100644 microbe/microbe.h create mode 100644 microbe/optimizer.cpp create mode 100644 microbe/optimizer.h create mode 100644 microbe/parser.cpp create mode 100644 microbe/parser.h create mode 100644 microbe/pic14.cpp create mode 100644 microbe/pic14.h create mode 100644 microbe/traverser.cpp create mode 100644 microbe/traverser.h create mode 100644 microbe/variable.cpp create mode 100644 microbe/variable.h (limited to 'microbe') 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 +#include + +/** +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 +#include +#include + +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("[^=>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 + 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 + +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 * + * 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 "optimizer.h" +#include "pic14.h" + +#include +#include + +#include +#include +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 = ""; + 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="<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="<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(*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"<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="< 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(current) || dynamic_cast(current) ) + { +// cout << "Added return link" << endl; +// cout << " FROM: " << current->code() << endl; +// cout << " TO: " << returnPoint->code() << endl; + returnPoint->addInputLink( current ); + return; + } + if ( dynamic_cast(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 * + * 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 INSTRUCTION_H +#define INSTRUCTION_H + +#include +#include +#include +#include + +class Code; +class CodeIterator; +class CodeConstIterator; +class Instruction; +class PIC14; + +typedef QValueList 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 +#include +#include +#include + +#include +#include +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 +#include +#include + +#include +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 +#include +// #include + +#include +#include +#include + +class QString; +class BTreeBase; +class BTreeNode; +class Code; +class PIC14; +class PortPin; + +typedef QValueList PortPinList; + +typedef QValueList VariableList; +typedef QMap AliasMap; + +enum ExprType +{ + unset = 1, + working = 2, + number = 3, + variable = 4, + extpin = 5, + keypad = 6, +}; + + +class SourceLine; +typedef QValueList 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 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 + +#include +#include +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="<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(*it) ) + referencedLabels << ins->label(); + else if ( Instr_call * ins = dynamic_cast(*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(*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(*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(*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 \""<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(*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 \""<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(*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 +#include +#include +#include +#include +#include + +#include +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 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 StatementDefinition; +typedef QMap DefinitionMap; +typedef QMap 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 +#include +#include +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="<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\")="<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 +#include +#include + +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 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 +#include + +class PortPin; +typedef QValueList 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 VariableList; + +#endif -- cgit v1.2.1