You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ktechlab/microbe/pic14.cpp

1197 lines
34 KiB

/***************************************************************************
* Copyright (C) 2004-2005 by Daniel Clarke *
* daniel.jc@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#include "instruction.h"
#include "parser.h"
#include "pic14.h"
#include <assert.h>
#include <kdebug.h>
#include <iostream>
using namespace std;
bool LEDSegTable[][7] = {
{ 1, 1, 1, 1, 1, 1, 0 },
{ 0, 1, 1, 0, 0, 0, 0 }, // 1
{ 1, 1, 0, 1, 1, 0, 1 }, // 2
{ 1, 1, 1, 1, 0, 0, 1 }, // 3
{ 0, 1, 1, 0 ,0, 1, 1 }, // 4
{ 1, 0, 1, 1, 0, 1, 1 }, // 5
{ 1, 0, 1, 1, 1, 1, 1 }, // 6
{ 1, 1, 1, 0, 0, 0, 0 }, // 7
{ 1, 1, 1, 1, 1, 1, 1 }, // 8
{ 1, 1, 1, 0, 0, 1, 1 }, // 9
{ 1, 1, 1, 0, 1, 1, 1 }, // A
{ 0, 0, 1, 1, 1, 1, 1 }, // b
{ 1, 0, 0, 1, 1, 1, 0 }, // C
{ 0, 1, 1, 1, 1, 0, 1 }, // d
{ 1, 0, 0, 1, 1, 1, 1 }, // E
{ 1, 0, 0, 0, 1, 1, 1 } // F
};
PIC14::PIC14( Microbe * master, Type type )
{
mb = master;
m_pCode = 0l;
m_type = type;
}
PIC14::~PIC14()
{
}
PortPin PIC14::toPortPin( const TQString & portPinString )
{
TQString port;
int pin = -1;
// In form e.g. RB3
if ( portPinString.length() == 3 )
{
port = TQString("PORT%1").arg( portPinString[1].upper() );
pin = TQString( 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 TQString & _text )
{
TQString 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 << TQString("%1 is not a known PIC identifier\n").arg(_text).local8Bit();
return unknown;
}
TQString 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 TQStringList &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);
TQStringList::ConstIterator interruptsEnd = interrupts.end();
for( TQStringList::ConstIterator it = interrupts.begin(); it != interruptsEnd; ++it )
{
// Is the interrupt's flag bit set?
m_pCode->append(new Instr_btfsc("INTCON",TQString::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 TQString &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 TQString & 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 TQString & trisName ) const
{
return ( trisName == "TRISA" || trisName == "trisa" ||
trisName == "TRISB" || trisName == "trisb" );
}
bool PIC14::isValidInterrupt( const TQString & 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 TQString &label)
{
m_pCode->append( new Instr_goto(label) );
}
void PIC14::Slabel(const TQString &label)
{
// std::cout << k_funcinfo << "label="<<label<<'\n';
m_pCode->queueLabel( label, Code::Middle );
}
void PIC14::Send()
{
m_pCode->append( new Instr_sleep() );
}
void PIC14::Ssubroutine( const TQString &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 TQString &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",TQString::number(interruptNameToBit(procName,true))) );
m_pCode->merge( subCode, Code::Subroutine );
m_pCode->append( new Instr_goto("_interrupt_end"), Code::Subroutine );
}
void PIC14::Scall(const TQString &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(),TQString::number(portPin.pin()) ) );
else
m_pCode->append( new Instr_bcf( portPin.port(), TQString::number(portPin.pin()) ) );
}
void PIC14::rearrangeOpArguments( TQString * val1, TQString * val2, LocationType * val1Type, LocationType * val2Type)
{
if( *val2Type == work && *val1Type != work )
{
LocationType tempType = *val2Type;
TQString tempVal = *val2;
*val2Type = *val1Type;
*val2 = *val1;
*val1Type = tempType;
*val1 = tempVal;
}
}
void PIC14::add( TQString val1, TQString 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 TQString & val1, const TQString & 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 TQString & val)
{
m_pCode->append(new Instr_movlw(val.toInt( 0, 0 )));
}
void PIC14::assignVar(const TQString &val)
{
m_pCode->append(new Instr_movf(val,0));
}
void PIC14::saveToReg(const TQString &dest)
{
m_pCode->append(new Instr_movwf(dest));
}
void PIC14::saveResultToVar( const TQString & var )
{
m_pCode->append( new Instr_movwf( var ) );
}
void PIC14::mul(TQString val1, TQString 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 TQString & val1, const TQString & 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 TQString &val1, const TQString &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 TQString &val1, const TQString &val2, LocationType val1Type, LocationType val2Type )
{
ifInitCode( val1, val2, val1Type, val2Type );
const TQString labelEnd = mb->uniqueLabel()+"_endif";
const TQString 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 TQString &val1, const TQString &val2, LocationType val1Type, LocationType val2Type )
{
ifInitCode( val1, val2, val1Type, val2Type );
const TQString labelEnd = mb->uniqueLabel()+"_endif";
const TQString 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 TQString &val1, const TQString &val2, LocationType val1Type, LocationType val2Type )
{
ifInitCode( val1, val2, val1Type, val2Type );
const TQString labelEnd = mb->uniqueLabel()+"_endif";
const TQString 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 TQString &val1, const TQString &val2, LocationType val1Type, LocationType val2Type )
{
cout << k_funcinfo << endl;
ifInitCode( val1, val2, val1Type, val2Type );
const TQString labelEnd = mb->uniqueLabel()+"_endif";
const TQString 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 TQString &val1, const TQString &val2, LocationType val1Type, LocationType val2Type )
{
ifInitCode( val1, val2, val1Type, val2Type );
const TQString labelEnd = mb->uniqueLabel()+"_endif";
const TQString 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 TQString &val1, const TQString &val2, LocationType val1Type, LocationType val2Type )
{
ifInitCode( val1, val2, val1Type, val2Type );
const TQString labelEnd = mb->uniqueLabel()+"_endif";
const TQString 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 TQString &expression)
{
TQString result;
TQString 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 TQString &expression)
{
TQString result;
TQString 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 TQString &expression)
{
m_parser->compileConditionalExpression( expression, ifCode, elseCode );
}
void PIC14::Sfor( Code * forCode, Code * initCode, const TQString &expression, const TQString &variable, const TQString &step, bool stepPositive)
{
TQString 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)
{
TQString 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(), TQString::number( portPin.pin() ) ));
//result +=instruction((TQString)(NOT?"btfsc":"btfss")+"\t"+port+","+pin);
else
m_pCode->append(new Instr_btfss( portPin.port(), TQString::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 )
{
TQString 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 );
TQString subName = TQString("__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;
TQString portName = TQString("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 + TQString("_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 + TQString("_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
TQString subName = TQString("__wait_read_keypad_%1").arg( pinMap.name() );
TQString waitName = TQString("__wait_keypad_%1").arg( pinMap.name() );
TQString readName = TQString("__read_keypad_%1").arg( pinMap.name() );
m_pCode->append( new Instr_call( subName ) );
if ( m_pCode->instruction( subName ) )
return;
//BEGIN Wait until read subroutine
m_pCode->queueLabel( subName, Code::Subroutine );
// Read current key (if any) from keypad and save to temporary variable
m_pCode->append( new Instr_call( readName ), Code::Subroutine );
m_pCode->append( new Instr_movwf( "__m" ), Code::Subroutine );
// Test if any key was pressed; if not, then start again
// std::cout << "mb->alias(\"Keypad_None\")="<<mb->alias("Keypad_None") << std::endl;
m_pCode->append( new Instr_sublw( mb->alias("Keypad_None").toInt( 0, 0 ) ), Code::Subroutine );
m_pCode->append( new Instr_btfsc( "STATUS","Z" ), Code::Subroutine );
m_pCode->append( new Instr_goto( subName ), Code::Subroutine );
m_pCode->append( new Instr_goto( waitName ), Code::Subroutine );
//END Wait until read subroutine
//BEGIN Wait until released subroutine
m_pCode->queueLabel( waitName, Code::Subroutine );
Sdelay( 10000, Code::Subroutine ); // 10 milliseconds for debouncing
// Key was pressed; now we wait until the key is released again
m_pCode->append( new Instr_call( readName ), Code::Subroutine );
m_pCode->append( new Instr_sublw( mb->alias("Keypad_None").toInt( 0, 0 ) ), Code::Subroutine );
m_pCode->append( new Instr_btfss( "STATUS","Z" ), Code::Subroutine );
m_pCode->append( new Instr_goto( waitName ), Code::Subroutine );
m_pCode->append( new Instr_movf( "__m", 0 ), Code::Subroutine );
m_pCode->append( new Instr_return(), Code::Subroutine );
//END Wait until released subroutine
if ( m_pCode->instruction( readName ) )
return;
//BEGIN Read current value of keypad subroutine
m_pCode->queueLabel( readName, Code::Subroutine );
// Make the four row lines low
for ( unsigned row = 0; row < 4; ++ row )
{
PortPin rowPin = pinMap.portPinList()[row];
m_pCode->append( new Instr_bcf( rowPin.port(), TQString::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(), TQString::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(), TQString::number( colPin.pin() ) ), Code::Subroutine );
m_pCode->append( new Instr_retlw( mb->alias( TQString("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(), TQString::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 TQString &r_val1, const TQString &val2, bool val1IsNum, bool val2IsNum )
{
TQString 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");
TQString 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 TQString &var )
{
m_pCode->append(new Instr_incf(var,1) );
}
void PIC14::SdecVar( const TQString &var )
{
m_pCode->append(new Instr_decf(var,1) );
}
void PIC14::SrotlVar( const TQString &var )
{
m_pCode->append(new Instr_rlf(var,1));
}
void PIC14::SrotrVar( const TQString &var )
{
m_pCode->append(new Instr_rrf(var,1));
}
void PIC14::Stristate(const TQString &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 TQString &raw)
{
m_pCode->append(new Instr_asm(raw));
}
//BEGIN class PortPin
PortPin::PortPin( const TQString & 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