Menu

[r1761]: / GCBASIC / trunk / gcbasic.bas  Maximize  Restore  History

Download this file

21431 lines (18314 with data), 796.2 kB

/'GCBASIC - A BASIC Compiler for microcontrollers
  Copyright (C) 2006 - 2025 Hugh Considine, Evan R. Venn and the GCBASIC team
  
  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  
  If you have any questions about the source code, please email me: hconsidine at internode.on.net
  Any other questions, please email me or see the GCBASIC forums.
  '/

'Array sizes
  #Define MAX_PROG_PAGES 20
'Number of buckets to use in hash maps - increasing makes operations faster but uses more RAM
  #Define HASH_MAP_BUCKETS 512

  #Define RESERVED_WORDS 1024

  #Define MAXTABLEITEMS 65536
  #DEFINE MAXPARAMS 50

'Start of Types
  Type PICASInc
    Value as String
    NumVal as Integer
  End Type

  Type PICASCfg
    Value as String
    State as String
    Config as String
  End Type

  Type PICASPatch
    Source as String
    Destination as String
  End Type

  Dim Shared ReverseIncFileLookup ( 5000) as PICASInc
  Dim Shared ReverseCfgFileLookup ( 10000) as PICASCfg
  Dim Shared Patches(100) as PICASPatch

  '31k types
  Type _31kCfg
    'Chip32Support_OSC,Chip32Support_Register, Chip32Support_ANDMask,Chip32Support_ORValue
    State as Integer
    OSCType as String
    OSCRegister as String
    Value as Integer
  End Type
  Dim Shared _31kSupported as _31kCfg

  'Type for sections of code returned from subs
  Type LinkedListElement
    Value As String
    NumVal As Integer
    MetaData As Any Pointer
    Prev As LinkedListElement Pointer
    Next As LinkedListElement Pointer
  End Type

  Type HashMap
    Buckets(HASH_MAP_BUCKETS) As LinkedListElement Pointer
  End Type

  'Meta data for program line
  Type ProgLineMeta
    RequiredBank As Integer 'Bank chip needs for this icon, -1 if N/A
    VarInBank As String 'Variable in the bank that is to be changed to

    IsAutoPageSel As Integer 'Set to -1 if line is automatically added pagesel

    'List of commands that lead to this one
    PrevCommands As LinkedListElement Pointer
    'List of commands that this one leads to
    NextCommands As LinkedListElement Pointer

    AsmCommand As Integer 'Index of asm instruction on line. -1 if none.
    LineSize As Integer 'Size of line in words.

    isLabel as Integer
    isPICAS as Integer
    IsESEG  as Integer

    OrgLine as String

    RawConfig as string

    Conditional as Integer

  End Type

  Type ConstMeta
    Value As String
    Startup As String
  End Type

  Type OriginType
    FileNo As Integer
    LineNo As Integer
    SubNo As Integer
  End Type

  Type SourceFileType
    FileName As String
    RequiresConversion As Integer = 0

    InitSub As String
    InitSubUsed As Integer
    InitSubPriority As Integer = 100
    InitSubFound As Integer = 0

    IncludeOrigin As String

    OptionExplicit As Integer

    UserInclude as Integer = 0
    SystemInclude as Integer = 0
    Ignore as Integer = 0

    RequiredModules As LinkedListElement Pointer
  End Type

  Type VariableType
    Name As String
    Type As String
    Size As Integer
    Pointer As String
    Alias As String
    Origin As String

    FixedLocation As Integer
    Location As Integer
    BitVarLocation As String
    IsSharedBank As Integer
    ExplicitDeclaration As Integer

    FixedSize As Integer
    Used As Integer

    NeedsSequentialLoc As Integer ' Set if individual bits accessed, and must have bytes in sequential order
    UndeclaredError As Integer ' Set if option explicit used and this variable wasn't declared

    AllocOnly as Integer  'Set to -1 if this variable is an ALLOC (allocation memory only)

  End Type

  Type VariableListElement
    Name As String
    Value As String
    IsArray As Integer
    SharedMemory As Integer = 0
  End Type

  Type IntData
    Vector As String
    VectorLoc As Integer
    EventName As String
    EnableBit As String
    FlagBit As String
    Handler As String
  End Type

  Type SubParam
    Name As String
    Type As String
    Dir As Integer '1 - in, 2 - out, 3 - in/out
    Default As String
  End Type

  Type SubType
    Name As String
    SourceFile As Integer
    Origin As String

    IsMacro As Integer
    IsFunction As Integer
    IsData As Integer
    ReturnType As String
    Params(50) As SubParam
    ParamCount As Integer
    Overloaded As Integer

    'Variables in subroutine
    Variables As HashMap
    VarsRead As Integer

    'Flags
    Required As Integer
    Compiled As Integer
    NoReturn As Integer
    HasFinalGoto As Integer

    'If ends in goto, record Sub jumped to
    FinalGotoDest As String

    'If overloaded function, alias for return value
    ReturnAlias As String

    'Call tree
    CallList As LinkedListElement Pointer 'Outgoing calls
    CallerList As LinkedListElement Pointer 'Incoming calls

    'Assembly bit variable used to save IntOn/IntOff state
    IntStateSaveVar As String

    'Original BASIC size (for compilation report)
    OriginalLOC As Integer

    'Program memory page selection hints
    HexSize As Integer
    DestPage As Integer
    FirstPage As Integer
    LocationSet As Integer
    MaxHexSize As Integer 'Highest recorded size
    CallsFromPage(MAX_PROG_PAGES) As Integer 'Record calls from each page

    'Temporary variables used (replace if they overlap with variables in a calling subroutine)
    TemporaryVars As LinkedListElement Pointer

    'Sub code
    CodeStart As LinkedListElement Pointer

  End Type

  Type DataTableType
    Name As String
    Type As String
    Origin As String
    StoreLoc As Integer '0 = Flash, 1 = Data EEPROM
    Used As Integer
    SourceFile As String 'Used if table loaded from file

    RawItems As LinkedListElement Pointer
    CurrItem As LinkedListElement Pointer

    Items As Integer
    Item(MAXTABLEITEMS) As LongInt
    IsEEPromData As Integer
    IsData As Integer
    FixedLoc As Integer
  End Type

  Type ProgString
    Value As String
    Used As Integer
  End Type

  Type SubCallType
    Called As SubType Pointer
    Caller As SubType Pointer
    CalledID As Integer
    Origin As String
    Param(MAXPARAMS, 2) As String
    Params As Integer
    CallSig As String
  End Type

  Type SysVarType
    Name As String
    Location As Integer
    Parent As String
    AVRAlias As Integer
  End Type

  Type AsmCommand
    Syntax As String
    Words As Integer
    Word(4) As String

    Cmd As String
    Params As Integer
    Param(25) As String

    FamilyVariant(10) As Integer
    FamilyVariants As Integer = 0

    Alternative As AsmCommand Pointer 'Need this to allow for multiple commands with same name
  End Type

  Type ConfigOp
    Op As String
    Loc As Integer
    Val As Integer
  End Type

  Type ConfigSetting
    Name As String 'Name of config setting
    Setting As LinkedListElement Pointer 'Points to current option
    Options As LinkedListElement Pointer 'List of potential settings
  End Type

  Type CalcVar
    MaxType As String
    Status As String
    CurrentType As String
    CurrentValue As String
    High As Integer
  End Type

  Type ExternalTool
    Name As String
    Type As String
    DispName As String
    Cmd As String
    Params As String
    WorkingDir As String
    ExtraParam(5, 2) As String
    ExtraParams As Integer

    'Conditions for use
    UseIf As String

    'Allow programmers to require config or option settings
    ProgConfig As String
    ProgOptions As String
  End Type

  Type FileConverterType
    Name As String
    Desc As String
    InFormat(10) As String
    InFormats As Integer
    OutFormat As String
    ExeName As String
    Params As String
    DeleteTarget As Integer
  End Type

  'Type to store generated code
  Type CodeSection
    CodeList As LinkedListElement Pointer
    CodeEnd As LinkedListElement Pointer
  End Type

  'Type to store a program memory page
  Type ProgMemPageType
    StartLoc As Integer
    EndLoc As Integer
    CodeSize As Integer
    MaxSize As Integer
    PageSize as Integer
  End Type

  'Type to store a RAM bank
  Type DataMemBankType
    StartLoc As Integer 'Start address of bank
    EndLoc As Integer 'End address of bank
    DataSize As Integer 'Amount of bank currently used
  End Type

  'Type to store info on which page is best
  Type AllocationOrderType
    Page As Integer
    Calls As Integer
  End Type

  'Type to store usage info on an I/O pin
  Type PinDirType
    'Name of the pin stored in LinkedListElement
    'This type used as metadata
    'Record pin reads and writes
    WrittenTo As Integer
    ReadFrom As Integer
    'Set to -1 if whole port set, but direction may not be known
    WholePort As Integer
    'Record pin direction settings
    SetOut As Integer
    SetIn As Integer
    'Allowed directions (from chip data)
    AllowedDirections As String
    'If Dir command sets entire port to constant, store here
    'Set to -1 if set by a variable
    WholePortDir As Integer
  End Type

'Subs in this file
  declare Sub Add18FBanks(CompSub As SubType Pointer)
  declare Sub AddBankCommands(CompSub As SubType Pointer)
  declare Sub AddDataBlocks ( ByRef CurrLine As LinkedListElement Pointer, ByRef CurrPage As Integer, ByRef CurrPagePos As Integer, DataBlockCount as Integer, NonChipFamily16DataBlocksNotAdded As Integer )
  declare Sub AddMainEndCode
  declare Sub AddMainInitCode
  declare Sub AddPageCommands(CompSub As SubType Pointer)
  declare Sub AddInterruptCode
  declare Sub AddSysVarBits (CompSub As SubType Pointer)
  declare Sub BuildMemoryMap
  declare Sub CalcConfig
  declare Sub CalcOps (OutList As CodeSection Pointer, SUM As String, AV As String, Ops As String, OriginIn As String, NeverLast As Integer)
  declare Function CalcLineSize(CurrLine As String, ThisSubPage As Integer, CallPos As AsmCommand Pointer = 0, GotoPos As AsmCommand Pointer = 0) As Integer
  declare Sub CalcSubSize(CurrSub As SubType Pointer)
  declare Function CastOrder (InType As String) As Integer
  declare Sub CheckConstName (ConstName As String, Origin As String)
  declare Sub CheckClockSpeed
  declare Sub CompileProgram
  declare Sub CompileSubroutine(CompSub As SubType Pointer)
  declare Sub CompileCalc (SUM As String, AV As String, Origin As String, ByRef OutList As CodeSection Pointer = 0, NeverLast As Integer = 0)
  declare Function CompileCalcAdd(CodeSection As CodeSection Pointer, V1 As String, Act As String, V2 As String, Origin As String, Answer As String) As String
  declare Function CompileCalcCondition(CodeSection As CodeSection Pointer, V1 As String, Act As String, V2 As String, Origin As String, Answer As String) As String
  declare Function CompileCalcLogic(CodeSection As CodeSection Pointer, V1 As String, Act As String, V2 As String, Origin As String, Answer As String) As String
  declare Function CompileCalcMult(CodeSection As CodeSection Pointer, V1 As String, Act As String, V2 As String, Origin As String, Answer As String) As String
  declare Function CompileCalcUnary(CodeSection As CodeSection Pointer, Act As String, V2 As String, Origin As String, AnswerIn As String) As String
  declare Function CompileConditions (Condition As String, IfTrue As String, Origin As String, CompSub As SubType Pointer = 0) As LinkedListElement Pointer
  declare Sub CompileDim (CurrSub As SubType Pointer)
  declare Sub CompileDir (CompSub As SubType Pointer)
  declare Sub CompileDo (CompSub As SubType Pointer)
  declare Sub CompileExitSub (CompSub As SubType Pointer)
  declare Sub CompileFor (CompSub As SubType Pointer)
  declare Sub CompileGoto (CompSub As SubType Pointer)
  declare Sub CompileIF (CompSub As SubType Pointer)
  declare Sub CompileIntOnOff (CompSub As SubType Pointer)
  declare Sub CompileOn (CompSub As SubType Pointer)
  declare Sub CompilePot (CompSub As SubType Pointer)
  declare Sub CompileReadTable (CompSub As SubType Pointer)
  declare Sub CompileRepeat (CompSub As SubType Pointer)
  declare Sub CompileReturn (CompSub As SubType Pointer)
  declare Sub CompileRotate (CompSub As SubType Pointer)
  declare Sub CompileSelect (CompSub As SubType Pointer)
  declare Sub CompileSet (CompSub As SubType Pointer)
  declare Function CompileString (InLine As String, Origin As String) As LinkedListElement Pointer
  declare Function CompileSubCall (InCall As SubCallType Pointer) As LinkedListElement Pointer
  declare Sub CompileSubCalls (CompSub As SubType Pointer)
  declare Sub CompileTables
  declare Sub CompileVars (CompSub As SubType Pointer)
  declare Function CompileVarSet (SourceIn As String, Dest As String, Origin As String, InvertBitCopy As Integer = 0) As LinkedListElement Pointer
  declare Sub CompileWait (CompSub As SubType Pointer)
  declare Function CompileWholeArray (InLine As String, Origin As String) As LinkedListElement Pointer
  declare Function ConfigNameMatch(ConfigIn As String, ConfigNameIn As String) As Integer
  declare Function ConfigValueMatch(ConfigIn As String, ConfigValueIn As String, MatchAny As Integer = 0) As Integer
  declare Sub CreateCallTree
  declare Sub CreateReservedWordsList
  declare Sub DisplayProgram
  declare Sub DisplayCallTree
  declare Sub ExtAssembler
  declare Sub PICASAssembler
  declare Sub ExtractParameters(ByRef NewSubCall As SubCallType, CalledSubName As String, CallParams As String, Origin As String)
  declare Sub FinalOptimise
  declare Sub FindAssembly (CompSub As SubType Pointer)
  declare Function FindPotentialBanks(CurrLine As LinkedListElement Pointer, OutList As LinkedListElement Pointer = 0, CheckedLines As LinkedListElement Pointer = 0) As LinkedListElement Pointer
  declare Sub FindUncompiledLines
  declare Function FixBit (InBit As String, Origin As String) As String
  declare Sub FixFunctions (CompSub As SubType Pointer)
  declare Sub FixPointerOps (CompSub As SubType Pointer)
  declare Sub FixSinglePinSet
  declare Sub FixTemporaryVariables
  declare Sub FreeCalcVar (VarName As String)
  declare Function GenerateArrayPointerSet(DestVar As String, DestPtr As Integer, CurrSub As SubType Pointer, Origin As String) As LinkedListElement Pointer
  declare Function GenerateAutoPinDir As LinkedListElement Pointer
  declare Function GenerateBitSet(BitNameIn As String, NewStatus As String, Origin As String, CurrSub As SubType Pointer = 0, SetStatus As Integer = -1) As LinkedListElement Pointer
  declare Function GenerateExactDelay(ByVal Cycles As Integer) As LinkedListElement Pointer
  declare Function GenerateMultiSet(SourceData As String, DestVar As String, Origin As String, CurrSub As SubType Pointer, CanSkip As Integer) As LinkedListElement Pointer
  declare Function GenerateVectorCode As LinkedListElement Pointer
  declare Function GetCalcType(VT1 As String, Act As String, VT2 As String, AnswerType As String) As String
  declare Function GetCalcVar (VarTypeIn As String) As String
  declare Function GetCalledSubs(CurrSub As SubType Pointer, ExistingList As LinkedListElement Pointer = 0, FindCallers As Integer = 0) As LinkedListElement Pointer
  declare Function GetDestSub(Origin As String) As Integer
  declare Sub GetEqConfig
  declare Function GetLabelList(CompSub As SubType Pointer) As LinkedListElement Pointer
  declare Function GetLinearLoc(Location As Integer) As Integer
  declare Function GetNonLinearLoc(Location As Integer) As Integer
  declare Function GetMetaData(CurrLine As LinkedListElement Pointer) As ProgLineMeta Pointer
  declare Function GetPinDirection(PinNameIn As String) As PinDirType Pointer
  declare Function GetRealIOName(InName As String) As String
  declare Function GetRegisterLoc(RegName As String) As Integer
  declare Function GetSysVar(VarName As String) As SysVarType Pointer
  declare Function GetSysVarName(Address as Integer) As String
  declare Function GetSysVarAliasName(Lookup as String) As String
  declare Function GetSub(Origin As String) As String
  declare Function GetSubFullName(SubIndex As Integer) As String
  declare Function GetSubID(Origin As String) As Integer
  declare Function GetSubSig(CurrentSub As SubType) As String
  declare Function GetSubParam (ParamIn As String, ForceIn As Integer) As SubParam
  declare Function GetTool(ToolName As String) As ExternalTool Pointer
  declare Sub InitCompiler
  declare Function IsArray (VarName As String, CurrSub As SubType Pointer) As Integer
  declare Function IsNonBanked(Location As Integer) As Integer
  declare Function IsInAccessBank(VarNameIn As String) As Integer
  declare Function IsIOPinName(PinName As String) As Integer
  declare Function IsIOReg (RegNameIn As String) As Integer
  declare Function IsIORegDX (RegNameIn As String) As Integer
  declare Function IsLowIOReg (RegNameIn As String) As Integer
  declare Function IsLowRegister(VarName As String) As Integer
  declare Function IsRegister (VarName As String) As Integer
  declare Function IsString (InData As String, CurrSub As SubType Pointer) As Integer
  declare Function IsUnaryOp (InData As String) As Integer
  declare Sub LoadConverters
  declare Function LocationOfSub (SubNameIn As String, SubSigIn As String, Origin As String = "", AllowVague As Integer = 0) As Integer
  declare Sub LogError(InMessage As String, Origin As String = "")
  declare Sub LogOutputMessage(InMessage As String)
  declare Sub LogWarning(InMessage As String, Origin As String = "")
  declare Sub MergeSubroutines
  declare Function Message (InData As String) As String
  declare Function ModeAVR As Integer
  declare Function ModePIC As Integer
  declare Function ModeZ8 As Integer
  declare Function NewCodeSection As CodeSection Pointer
  declare Function NewProgLineMeta As ProgLineMeta Pointer
  declare Function NewSubroutine(SubName As String) As SubType Pointer
  declare Sub OptimiseCalls
  declare Sub OptimiseIF(CompSub As SubType Pointer = 0)
  declare Sub OptimiseIncrement(CompSub As SubType Pointer)
  declare Sub PreparePageData
  declare Sub PrepareProgrammer
  declare Sub ProcessArrays (CompSub As SubType Pointer)
  declare Sub ProcessWords ()
  declare Function PutInRegister(ByRef OutList As LinkedListElement Pointer, SourceValue As String, RegType As String, Origin As String) As String
  declare Sub ReadChipData
  declare Sub ReadPICASChipData
  declare Sub ReadOptions(OptionsIn As String)
  declare Sub RecordSubCall(CompSub As SubType Pointer, CalledSub As SubType Pointer)
  declare Function ReplaceFnNames(InName As String) As String
  declare Function RequestSub(Requester As SubType Pointer, SubNameIn As String, SubSigIn As String = "") As Integer
  declare Sub RetrySubRequests
  declare Sub SetCalcTempType (CalcVar As String, NewType As String)
  declare Function SetStringPointers (V1 As String, V2 As String, CurrSub As SubType Pointer, Origin As String) As LinkedListElement Pointer
  declare Sub ShowBlock (BlockIn As String)
  declare Sub SplitLines (CompSub As SubType Pointer)
  declare Function TempRemove(Removed As String) As String
  declare Sub TidyProgram
  declare Sub TidySubroutine(CompSub As SubType Pointer)
  declare Function TranslateFile(InFile As String) As String
  declare Function TypeOfVar (VarName As String, CurrSub As SubType Pointer) As String
  declare Function TypeOfValue (ValueNameIn As String, CurrentSub As SubType Pointer, SingCharString As Integer = 0) As String
  declare Sub UpdateOutgoingCalls (CompSub As SubType Pointer)
  declare Sub UpdateSubMap
  declare Sub UpgradeCalcVar (VarName As String, VarType As String)
  declare Sub ValueChanged(VarName As String, VarValue As String)
  declare Sub ValidateParameterIsValid ( inline as String, FunctionParam as String, Origin as String )
  declare Function VarAddress (ArrayNameIn As String, CurrSub As SubType Pointer) As VariableType Pointer
  declare Sub WriteAssembly
  declare Sub WriteCompilationReport
  declare Sub WriteErrorLog

'Subs in assembly.bi
  declare Sub AddAsmSymbol(SymName As String, SymValue As String)
  declare Sub AsmOptimiser (CompSub As SubType Pointer)
  declare Function AsmTidy (DataSource As String, StoredGCASM as integer = -1 ) As String
  declare Function ConfigTidy (DataSource As String ) As String
  declare Sub AssembleProgram
  declare Sub BuildAsmSymbolTable
  declare Function GetConfigBaseLoc As Integer
  declare Function IsASM (DataSource As String, ParamCount As Integer = -1) As AsmCommand Pointer
  declare Function IsASMConst (DataSource As String) As Integer
  declare Function IsHexConfigValid(HexFile As String, ConfigSettings As String) As Integer
  declare Function IsForVariant(FoundCmd As AsmCommand Pointer) As Integer

'Subs in variables.bi
  declare Function AddFinalVar(VarName As String, VarLoc As String, VarIsArray As Integer = 0) As Integer
  declare Sub AddVar(VarNameIn As String, VarTypeIn As String, VarSizeIn As Integer, VarSubIn As SubType Pointer, VarPointerIn As String, OriginIn As String, FixedLocation As Integer = -1, ExplicitDeclaration As Integer = 0, Used As Integer = -1)
  declare Sub AllocateRAM
  declare Function CalcAliasLoc(LocationIn As String) As Integer
  declare Function GetWholeSFR(BitName As String) As String
  declare Function GetSFRBitValue(BitName As String) As String
  declare Function HasSFR(SFRName As String) As Integer
  declare Function HasSFRBit(BitName As String) As Integer
  declare Function IsNumberString(gstring As String) As Integer
  declare Sub MakeSFR (UserVar As String, SFRAddress As Integer, AVRAlias As Integer = 0 )
  declare Sub RequestVariable(VarName As String, CurrSub As SubType Pointer)
  declare Function GetReversePICASIncFileLookupValue( address As integer ) As String

'Subs in preprocessor.bi
  declare Function LongToString(value as ulong) as string
  declare Sub AddConstant(ConstName As String, ConstValue As String, ConstStartup As String = "", ReplaceExisting As Integer = -1)
  declare Function CheckSysVarDef(ConditionIn As String) As String
  declare Sub LoadTableFromFile(DataTable As DataTableType Pointer)
  declare Sub OptimiseAVRDx ()
  declare Sub PrepareBuiltIn ()
  declare Sub PreProcessor ()
  declare Sub ProcessSame (DirectiveIn As String)
  declare Sub ReadTableValues
  declare Sub RemIfDefs ()
  declare Function RemoveSpacesfromCommands ( CO as String, Param as String ) As String
  declare Sub ReplaceConstants ()
  declare SUB ExpandShifts ()
  declare Function ReplaceConstantsLine (DataSourceIn As String, IncludeStartup As Integer) As String
  declare Sub RunScripts ()
  declare Sub TidyInputSource (CompSub As SubType Pointer)
  declare Sub TableString (DataSource As String, TF As String )

'Subs in utils.bi
  declare Function AddFullPath(CurrPath As String, FullPathIn As String = "") As String
  declare Sub Calculate (SUM As String)
  declare Function CountOccur (Source As String, Search As String, SearchWhole As Integer = 0) As Integer
  declare Function CountSubstring (Source As String, Search As String) As Integer
  declare Function DelType (InString As String) As String
  declare Function GetByte (DataSource As String, BS As Integer) As String
  declare Function GetElements(InData As String, DivChar As String = "", IncludeDividers As Integer = 0) As LinkedListElement Pointer
  declare Function GetFileLine(Origin As String) As String
  declare Function GetNextTempVar(CurrVar As String) As String
  declare Function GetOriginString(OriginIn As OriginType Pointer) As String
  declare Function GetDoubleBytes (InValue As Double) As ULongInt
  declare Function GetSingleBytes (InValue As Single) As UInteger
  declare Function GetString(StringName As String, UsedInProgram As Integer = -1) As String
  declare Sub GetTokens(InData As String, OutArray() As String, ByRef OutSize As Integer, DivChar As String = "", IncludeDividers As Integer = 0)
  declare Function GetTypeLetter(InType As String) As String
  declare Function GetTypeSize(InType As String) As Integer
  declare Function GetVarByteNumber(VarName As String) As Integer
  declare Function HashMapCreate As HashMap Pointer
  declare Function HashMapCalcHash(Key As String) As Integer
  declare Sub HashMapDestroy(Map As HashMap Pointer)
  declare Sub HashMapDelete(Map As HashMap Pointer, Key As String, DeleteMeta As Integer = -1)
  declare Function HashMapGet(Map As HashMap Pointer, Key As String) As Any Pointer
  declare Function HashMapGetStr(Map As HashMap Pointer, Key As String) As String
  declare Function HashMapSet OverLoad (Map As HashMap Pointer, Key As String, Value As String, ReplaceExisting As Integer = -1) As Integer
  declare Function HashMapSet OverLoad (Map As HashMap Pointer, Key As String, Value As Any Pointer, ReplaceExisting As Integer = -1) As Integer
  declare Function HashMapToList(Map As HashMap Pointer, Sorted As Integer = 0) As LinkedListElement Pointer
  declare Function IsCalc (Temp As String) As Integer
  declare Function IsCalcDivider (Temp As String) As Integer
  declare Function IsConst (Temp As String) As Integer
  declare Function IsDivider (Temp As String) As Integer
  declare Function IsFloatType(InType As String) As Integer
  declare Function IsIntType(InType As String) As Integer
  declare Function IsLet(Temp As String) As Integer
  declare Function IsSysTemp(VarNameIn As String) As Integer
  declare Function IsValidName(InName As String) As Integer
  declare Function IsValidValue(InValue As LongInt, TypeIn As String) As Integer
  declare Function LinkedListCreate As LinkedListElement Pointer
  declare Function LinkedListFind OverLoad (StartNode As LinkedListElement Pointer, SearchMeta As Any Pointer) As LinkedListElement Pointer
  declare Function LinkedListFind OverLoad (StartNode As LinkedListElement Pointer, SearchValue As String) As LinkedListElement Pointer
  declare Function LinkedListInsert OverLoad (Location As LinkedListElement Pointer, NewLine As String, NewNumVal As Integer = 0) As LinkedListElement Pointer
  declare Function LinkedListInsert OverLoad (Location As LinkedListElement Pointer, NewData As Any Pointer) As LinkedListElement Pointer
  declare Function LinkedListInsertList (Location As LinkedListElement Pointer, NewList As LinkedListElement Pointer, NewListEndIn As LinkedListElement Pointer = 0) As LinkedListElement Pointer
  declare Function LinkedListAppend (ListIn As LinkedListElement Pointer, NewList As LinkedListElement Pointer, NewListEndIn As LinkedListElement Pointer = 0) As LinkedListElement Pointer
  declare Function LinkedListDelete (Location As LinkedListElement Pointer, DeleteMeta As Integer = -1) As LinkedListElement Pointer
  declare Function LinkedListDeleteList (StartLoc As LinkedListElement Pointer, EndLoc As LinkedListElement Pointer) As LinkedListElement Pointer
  declare Sub LinkedListPrint(StartNode As LinkedListElement Pointer)
  declare Sub LinkedListPrintwithComments(StartNode As LinkedListElement Pointer)
  declare Function LinkedListSize(StartNode As LinkedListElement Pointer) As Integer
  declare Function MakeDec (DataSource As String) As LongInt
  declare Function MakeDecFloat (DataSource As String) As Double
  declare Function NCase(InValue As String) As String
  declare Function NextCodeLine(CodeLine As LinkedListElement Pointer) As LinkedListElement Pointer
  declare Function PrefIsYes(CheckVal As String, YesVal As Integer = -1) As Integer
  declare Sub Replace (DataVar As String, Find As String, Rep As String)
  declare Sub ReplaceAll (DataVar As String, Find As String, Rep As String)
  declare Function ReplaceToolVariables(InData As String, FNExtension As String = "", FileNameIn As String = "", Tool As ExternalTool Pointer = 0) As String
  declare Sub SCICONV (STemp As String)
  declare Function ShortFileName(InName As String) As String
  declare Function ShortName (NameIn As String) As String
  declare Function SubSigMatch (SubSigIn As String, CallSigIn As String) As Integer
  declare Sub WaitForKeyOrTimeout
  declare Function WholeINSTR (DataIn As String, FindIn As String, SearchAgain As Integer = -1) As Integer
  declare Function WholeInstrLoc(DataSource As String, FindTemp As String) As Integer
  declare Sub WholeReplace (DataVar As String, Find As String, Rep As String)
  declare Sub StringSplit(Text As String, Delim As String = " ", Count As Long = -1, Ret() As String)

'Initialise Misc Vars
  DIM SHARED As Integer FRLC, FALC, SBC, WSC, FLC, DLC, SSC, SASC, POC, MainSBC, CompiledSBC, InsertLineNo
  DIM SHARED As Integer COC, BVC, PCC, CVCC, TCVC, CAAC, ISRC, IISRC, RPLC, ILC, SCT
  DIM SHARED As Integer CSC, CV, COSC, MemSize, FreeRAM, FoundCount, PotFound, IntLevel
  DIM SHARED As Integer ChipGPR, ChipRam, ConfWords, DataPass, ChipFamily, ChipFamilyVariant, ChipSubFamily, PSP, ChipProg, IntOscSpeedValid, ChipMinimumBankSelect
  Dim Shared As Integer ChipPins, UseChipOutLatches, AutoContextSave, LaxSyntax, PICASdebug, PICASDEBUGmessageShown, DATfileinspection, NoSummary, ConfigDisabled, UserCodeOnlyEnabled, ChipIO, ChipADC, methodstructuredebug, floatcapability, compilerdebug, ChipAVRDX, overridelowleveldatfileextextension, overridelowleveldatfileextextensionmessage, overridetestdatfilemessage
  Dim Shared As Integer MainProgramSize, StatsUsedRam, RegBytesUsed = 0
  DIM SHARED As Integer VBS, MSGC, PreserveMode, SubCalls, IntOnOffCount, ExitValue, OutPutConfigOptions
  DIM SHARED As Integer UserInt, PauseOnErr, USDC, MRC, GCGB, ALC, DCOC, SourceFiles, IgnoreSourceFiles
  Dim Shared As Integer WarningsAsErrors, FlashOnly, SkipHexCheck, ShowProgressCounters, muteBanners, ExtendedVerboseMessages, MuteDonate
  DIM SHARED As Integer SubSizeCount, PCUpper, Bootloader, HighFSR, NoBankLocs
  DIM SHARED As Integer RegCount, IntCount, AllowOverflow, SysInt, HMult, AllowInterrupt
  Dim Shared As Integer ToolCount, ChipEEPROM, DataTables, ProgMemPages, PauseAfterCompile
  Dim Shared As Integer gUSDelaysInaccurate, IntOscSpeeds, PinDirShadows, CompileSkipped
  Dim Shared As Integer PauseTimeout, OldSBC, ReserveHighProg, HighTBLPTRBytes, FirstESEG
  Dim Shared As Single ChipMhz, ChipMaxSpeed, FileConverters
  Dim Shared As Single StartTime, CompEndTime, AsmEndTime, ProgEndTime
  Dim Shared As Double DebugTime, StatsUsedProgram
  Dim Shared As String ChipPICASDataFile,ChipPICASConfigFile,ChipPICASConfigFileV3,ChipPICASRoot, Conditionaldebugfile, LastOrigin
  Dim Shared As Integer StringConCatLengthAdapted = 0
  Dim shared As integer WholeINSTRdebug = 0
  Dim Shared As Boolean nonAsciiFound = False
  Dim Shared As String DefaultInitSys: DefaultInitSys = "InitSys"

'Assembler vars
  DIM SHARED As Integer ToAsmSymbols
  DIM SHARED As String  SelectedAssembler

'Code Array
  Dim Shared CompilerOutput As CodeSection Pointer
  DIM SHARED As LinkedListElement Pointer AsmProg, AsmProgLoc

'Sub arrays
  Dim Shared Subroutine(10000) As SubType Pointer: SBC = 0: MainSBC = 0: CompiledSBC = 0
  Dim Shared Subroutines As HashMap Pointer

'Processing Arrays
  DIM SHARED Constants As HashMap Pointer
  Dim Shared SourceFile(100) As SourceFileType: SourceFiles = 0
  Dim Shared IgnoreSourceFile(100) As SourceFileType: IgnoreSourceFiles = 0
  DIM SHARED TempData(300) As String
  DIM SHARED CheckTemp(300) As String
  Dim SHARED SysVars As HashMap Pointer
  DIM SHARED SysVarBits As HashMap Pointer
  DIM SHARED FILE(300) As String
  Redim SHARED FreeMem(1) As Integer
  Redim SHARED VarLoc(1) As Integer
  DIM SHARED MemRanges(100) As String: MRC = 0
  Dim Shared NoBankLoc(10) As DataMemBankType: NoBankLocs = 0
  Dim SHARED StringStore(0 To 1000) As ProgString: SSC = 0: SASC = 0
  Dim Shared ConfigOps(700) As ConfigOp: COC = 0
  Dim Shared ConfigSettings As LinkedListElement Pointer
  Dim Shared EqConfigSettings As LinkedListElement Pointer
  Dim Shared ChipConfigCode As CodeSection Pointer
  DIM SHARED DefCONFIG(700) As String: DCOC = 0
  DIM SHARED ConfigMask(20) As Integer
  DIM SHARED DataTable(500) As DataTableType: DataTables = 0
  DIM SHARED Messages(1 TO 2, 500) As String: MSGC = 0
  DIM SHARED ASMCommands As HashMap Pointer
  DIM SHARED ASMSymbols As HashMap Pointer
  Dim Shared ToAsmSymbol(500, 1 To 2) As String: ToAsmSymbols = 0
  DIM SHARED FinalVarList As HashMap Pointer
  DIM Shared FinalRegList(100) As VariableListElement: FRLC = 0
  DIM Shared FinalAliasList(8000) As VariableListElement: FALC = 0
  DIM SHARED PreserveCode(60000) As String: PCC = 0
  Dim Shared CalcVars(100) As CalcVar: TCVC = 0
  Dim Shared As String AllSysSubs, UsedSysSubs
  Dim Shared RegList(512, 2) As String: RegCount = 0
  Dim Shared CalcAtAsm(2000) As String: CAAC = 0
  Dim Shared CalcTempType(3) As String
  Dim Shared Interrupts(200) As IntData: IntCount = 0
  Dim Shared IntInitCode As CodeSection Pointer
  Dim Shared IntHandlerCode As CodeSection Pointer
  Dim Shared Tool(200) As ExternalTool: ToolCount = 0
  Dim Shared ToolVariables As LinkedListElement Pointer
  Dim Shared ProgMemPage(MAX_PROG_PAGES) As ProgMemPageType: ProgMemPages = 0
  Dim Shared IntOscSpeed(20) As Double: IntOscSpeeds = 0
  Dim Shared FileConverter(50) As FileConverterType: FileConverters = 0
  Dim Shared AttemptedCallList As LinkedListElement Pointer
  Dim Shared OutConfig(16) As String
  Dim Shared PinDirections As LinkedListElement Pointer
  Dim Shared PinDirShadow(20) As String
  Dim Shared GlitchFreeOutputs As HashMap Pointer
  Dim Shared ReservedWords( RESERVED_WORDS ) as String
  
'Shared Variables
  Dim Shared As String Star80
  Dim Shared As String Pad32

  Dim Shared As String ChipName, OSCType, CONFIG, Intrpt, gcOPTION, ChipProgrammerName
  Dim Shared As String ChipOscSource
  Dim Shared As String CDF, AFI, FI, OFI, HFI, ID, Version, buildVersion, ProgDir, CLD, LabelEnd, VersionSuffix
  Dim Shared As String PrgExe, PrgParams, PrgDir, AsmExe, AsmParams, PrgName, HexAppend
  Dim Shared As ExternalTool Pointer AsmTool, PrgTool
  Dim Shared As String CompReportFormat, globalSettingsFile

  Dim Shared As Integer CDFSupport = 0
  Dim Shared As Integer CDFFileHandle
  Dim Shared As Integer AFISupport = 0
  Dim Shared As Integer StoredGCASM = 0
  Dim Shared As Integer MakeHexMode = 1
  Dim Shared As Integer Columnwidth = 77
  Dim Shared As UByte   ReservedwordC = 0
  Dim Shared As Integer ChipMhzCalculated = 0
  Dim Shared As Integer DataBlockCount = 0
  Dim Shared As Integer RequiredModuleCheck = 1
  Dim Shared as Integer RandomNumber
'Config correct code
  Dim Shared as string adaptedConfigLine
  Dim Shared as string configarray()
  Dim Shared as Integer configarraycounter

'Support for #OPTION USERCODEONLY
  Dim Shared As String UserDefineStartLabel
  Dim As Integer CD, T, PD


'PICAS Converter misc vars
  Dim Shared as string currentLineElements()
  Dim Shared as Integer patchCounter = 0

'Constants
  #Define DISABLEOPTIMISATION "f122 jmp"
  #Define INVALIDARRAYVALUE -9999

  #Define MAX_OUTPUT_MESSAGES 200
  Dim Shared As String OutMessage(MAX_OUTPUT_MESSAGES)
  Dim Shared As Integer OutMessages, ErrorsFound

  #Define TYPECHECKSIZE 40

  const   ChipFamily18FxxQ10 as integer = 16100
  const   ChipFamily18FxxQ43 as integer = 16101
  const   ChipFamily18FxxQ41 as integer = 16102
  const   ChipFamily18FxxK42 as integer = 16103
  const   ChipFamily18FxxK40 as integer = 16104
  const   ChipFamily18FxxQ40 as integer = 16105
  const   ChipFamily18FxxQ84 as integer = 16106
  const   ChipFamily18FxxK83 as integer = 16107
  const   ChipFamily18FxxQ83 as integer = 16108
  const   ChipFamily18FxxQ71 as integer = 16109
  const   ChipFamily18FxxQ20 as integer = 16110
  const   ChipFamily18FxxQ24 as integer = 16111


'Compiler USER DEBUG constants
  const   cCOMPILECALCADD     as integer = 1
  const   cVAR_SET            as integer = 2
  const   cCALCOPS            as integer = 4
  const   cCOMPILECALCMULT    as integer = 8
  const   cGENERATEAUTOPINDIR as integer = 16
  const   cAVRDXDEBUG         as integer = 32
  const   cGCASMDEBUG         as integer = 64
  const   cCOMPILESUB         as integer = 128
  const   cCOMPILEUPDATESUBMAP as integer = 256
  const   cEXPANDSHIFTS        as integer = 512
  

  const   INSERTFILENOTOPEN = 1
  const   INSERTFILEOPEN    = 2
  const   INSERTFILEPROCESS = 3
  const   INSERTFILEEOF     = 4


'Add the other GCBASIC source files
  #include "utils.bi"
  #include "assembly.bi"
  #include "preprocessor.bi"
  #include "variables.bi"

'Include FreeBASIC libraries
  #include "file.bi"
  #Include "string.bi"

'Close any open files, delete old error log
  CLOSE
  IF Dir("ERRORS.TXT") <> "" THEN KILL "ERRORS.TXT"

'Get random number seed
  Randomize Timer

'SET GCBASIC VERSION
  Version = "2025.02.08"
  buildVersion = "1464"
'Construct compiler message for each Operating System
  #ifdef __FB_DARWIN__  'OS X/macOS
    #ifndef __FB_64BIT__
      Version = Version + " (Darwin 32 bit)"
    #else
      Version = Version + " (Darwin 64 bit)"
    #endif
  #elseif defined (__FB_FREEBSD__)
    #ifndef __FB_64BIT__
      Version = Version + " (FreeBSD 32 bit)"
    #else
      Version = Version + " (FreeBSD 64 bit)"
    #endif
  #elseif defined (__FB_LINUX__)
    #ifndef __FB_64BIT__
      Version = Version + " (Linux 32 bit)"
    #else
      Version = Version + " (Linux 64 bit)"
    #endif
  #elseif defined (__FB_WIN32__)  'Need to test first as WIN32 matches both 32 and 64 bit
    #ifdef __FB_64BIT__
        Version = Version + " (Windows 64 bit)"
    #else
        Version = Version + " (Windows 32 bit)"
    #endif
  #endif

  Version = Version + " : Build " + buildVersion


'Initialise assorted variables
  Star80 = ";********************************************************************************"
  Pad32 = "                                 "
  SysInt = 0
  IntLevel = 0
  AllowInterrupt = 0
  ErrorsFound = 0
  PinDirShadows = 0
  MainProgramSize = 0
  StatsUsedRam = 0
  StatsUsedProgram = 0
  UseChipOutLatches = -1
  AutoContextSave = -1
  LaxSyntax = 0
  PICASdebug = false
  methodstructuredebug = false
  floatcapability =  0 '  1 = singles, 2 = doubles, 4 = longint, 8 = uLongINT
  compilerdebug = 0
  overridelowleveldatfileextextension = 0
  overridelowleveldatfileextextensionmessage = 0
  overridetestdatfilemessage=0
  PICASDEBUGmessageShown = false
  DATfileinspection = true
  NoSummary = 0
  ReserveHighProg = 0
  ConfigDisabled = 0
  UserCodeOnlyEnabled = 0
  ExitValue = 0
  ToolVariables = LinkedListCreate
  CompileSkipped = 0
  OldSBC = -1
  DebugTime = 0
  MakeHexMode = 1
  Conditionaldebugfile = ""
  SelectedAssembler = "GCASM"
  VersionSuffix = ""
  ChipAVRDX = 0
  FirstESEG = 0      'Used to ensure only one .ESEG AVR control is added to AVR ASM
  AddConstant("CHIPASSEMBLER", SelectedAssembler )

  ChipProgrammerName=""
  _31kSupported.State = 0

'Initialise various size counters
  USDC = 0 'US delay loops
  RPLC = 0 'Repeat loops
  WSC = 0 'Wait Until/While loops
  DLC = 0 'Do loops
  SCT = 0 'Select Case
  gUSDelaysInaccurate = 0 'Set if variable len US delays will be wrong
  IntOnOffCount = 0 'Count IntOn/IntOff

'Show startup messages, InitCompiler and handle command line parameters
  StartTime = Timer
  InitCompiler

'Initialise code sections and lists
  CompilerOutput = NewCodeSection
  IntInitCode = NewCodeSection
  IntHandlerCode = NewCodeSection
  ChipConfigCode = NewCodeSection

  AttemptedCallList = LinkedListCreate
  ConfigSettings = LinkedListCreate
  PinDirections = LinkedListCreate
  Constants = HashMapCreate
  AsmProg = LinkedListCreate
  AsmProgLoc = AsmProg
  GlitchFreeOutputs = HashMapCreate

  SysVars = HashMapCreate
  SysVarBits = HashMapCreate
  ASMCommands = HashMapCreate

  CreateReservedWordsList()
  If  ErrorsFound Then Goto Fin 

'Call PreProcessor - Load files and tidy them up, a lot...
  PreProcessor

'If no PreProcessor error(s) then continue to next phase of compilation
  If Not ErrorsFound Then

    If FlashOnly Then
      Print Message("SkippingCompile")
      CompEndTime = Timer
      AsmEndTime = CompEndTime
      CompileSkipped = -1
      GoTo DownloadProgram
    End If

    'Compile
    CompileProgram

    'Allocate RAM
    IF VBS = 1 THEN PRINT : PRINT SPC(5); Message("AllocateRAM")
    AllocateRAM

    IF VBS = 1 THEN PRINT : PRINT SPC(5); Message("TidyCode")
    TidyProgram

    'Combine subs
    IF VBS = 1 THEN PRINT : PRINT SPC(5); Message("LocatingSubs")
    MergeSubroutines

    'Final optimisation
    FinalOptimise

    If VBS = 1 THEN PRINT : PRINT SPC(5); Message("WritingASM")
    WriteAssembly
    CompEndTime = Timer

    'error check.. has program exceeded avialable progmem
    If StatsUsedProgram + ReserveHighProg > ChipProg Then
      Dim Temp As String

      PRINT
      PRINT Message("Summary")
      PRINT SPC(5); Message("DataRead")
      PRINT SPC(10); Message("InLines") + Str(MainProgramSize)
      PRINT SPC(10); Message("Subs" ) + " User: " + Str( MainSBC - 1 ) + " ; System: " + Str(CompiledSBC - MainSBC ) + " of " + Str(SBC -  MainSBC - 1 )+ " ; Total: " + Str( MainSBC - 1 + CompiledSBC - MainSBC )
      PRINT SPC(5); Message("ChipUsage")
      Temp = Message("UsedProgram")
      Replace Temp, "%used%", Str(StatsUsedProgram + ReserveHighProg)
      Replace Temp, "%total%", Str(ChipProg)
      If ChipProg <> 0 Then Temp += Format((StatsUsedProgram + ReserveHighProg) / ChipProg, " (###.##%)")
      PRINT SPC(10); Temp
      Temp = Message("UsedRAM")
      Replace Temp, "%used%", Str(StatsUsedRam+RegBytesUsed)
      Replace Temp, "%total%", Str(ChipRAM)
      If ChipRAM <> 0 Then Temp += Format(StatsUsedRAM / ChipRAM, " (###.##%)")
      PRINT SPC(10); Temp


      LogError Message ("OutOfProgMemExceeded")
    End if

    'If no errors, show success message and assemble
    IF Not ErrorsFound THEN
      'Success message
      PRINT Message("CompiledSuccess");
      '  IF VBS = 0 THEN
      '    PRINT
      '  Else
        Dim Temp As String
        Temp = Trim(Str(CompEndTime - StartTime))
        IF LEN(Temp) > 4 Then Temp = Left(Temp, 5)
        PRINT Message("CompTime") + Temp + Message("CompSecs")
        If NoSummary = 0 Then
          PRINT
          PRINT Message("Summary")

          PRINT SPC(5); Message("DataRead")

          PRINT SPC(10); Message("InLines") + Str(MainProgramSize)
          PRINT SPC(10); Message("Subs" ) + " User: " + Str( MainSBC - 1 ) + " ; System: " + Str(CompiledSBC - MainSBC ) + " of " + Str(SBC -  MainSBC - 1 )+ " ; Total: " + Str( MainSBC - 1 + CompiledSBC - MainSBC )
          PRINT SPC(5); Message("ChipUsage")
          Temp = Message("UsedProgram")
          Replace Temp, "%used%", Str(StatsUsedProgram + ReserveHighProg)
          Replace Temp, "%total%", Str(ChipProg)
          If ChipProg <> 0 Then Temp += Format((StatsUsedProgram + ReserveHighProg) / ChipProg, " (###.##%)")
          PRINT SPC(10); Temp
          Temp = Message("UsedRAM")
          Replace Temp, "%used%",  Str(StatsUsedRam+RegBytesUsed)
          Replace Temp, "%total%", Str(ChipRAM)
          If ChipRAM <> 0 Then 
            If StatsUsedRam+RegBytesUsed = 0 Then
              Temp += " (0%)"
            Else
              Temp += Format( (StatsUsedRAM+RegBytesUsed) / ChipRAM, " (###.##%)")
            End If
          End If


          PRINT SPC(10); Temp


          OscType = ""
          If ModePIC Then
            If HashMapGet(Constants, "CHIPUSINGINTOSC") <> 0 Then
              OscType = " (" + Message("CRIntOsc") + ")"
            Else
              OscType = " (" + Message("CRExtOsc") + ")"
            End If
          End If
          If ChipAVRDX Then
            If HashMapGet(Constants, "CHIPUSINGINTOSC") <> 0 Then
              OscType = " (" + Message("CRIntOsc") + ")"
            Else
              OscType = " (" + Message("CRExtOsc") + ")"
            End If
          End If

          PRINT SPC(10);
          If ModePIC Then
              Print "OSC: " + ChipOscSource + ", " + Str(ChipMhz) + "Mhz" + OscType;
          Else
              Print "OSC: " + Str(ChipMhz) + "Mhz" + OscType;
          End If
          If ChipMhzCalculated = 0 Then
            Print
          Else
            Print " : Frequency calculated by compiler"
          End If
        End If
    '  END IF

      If ModeAVR then
            AFISupport = 0
      End IF

      If AsmExe <> "" and MakeHexMode = 1 Then

        IF UCase(AsmExe) = "GCASM" THEN
          'Internal assembler
          PRINT
          PRINT Message("MakeASM")
          AssembleProgram

        ELSEIF instr(UCase(AsmExe),"PIC-AS") > 0 THEN

          'AssembleProgram
          PRINT
          PRINT Message("MakeS")
          PICASAssembler
          'IF Not ErrorsFound THEN PRINT Message("PICASMSuccess")


        Else
          ExtAssembler
        END If

        AsmEndTime = Timer
        'If VBS = 1 Then
        ' Dim Temp As String
        ' Temp = Trim(Str(AsmEndTime - CompEndTime))
        ' IF LEN(Temp) > 4 Then Temp = Left(Temp, 5)
        ' PRINT Message("AsmTime") + Temp + Message("CompSecs")
        'End If

          IF Not ErrorsFound THEN
            Dim Temp As String
            Temp = Trim(Str(AsmEndTime - CompEndTime))
            IF LEN(Temp) > 4 Then Temp = Left(Temp, 5)
            PRINT Message("ASMSuccess") + Message("AsmTime") + Temp + Message("CompSecs")
          End If

      End If




    End If
    AsmEndTime = Timer

    'Download program
    DownloadProgram:
      IF (  PrgExe <> "" AND  Ucase(RIGHT(PrgExe,4)) <> "NONE"  AND AsmExe <> "" ) AND ErrorsFound = 0 THEN
        PRINT
        Dim Temp As String
        Temp = Message("SendToPIC")
        Replace Temp, "%PrgName%", Trim(Str(PrgName))
        PRINT Temp
        PrgExe = ReplaceToolVariables(PrgExe, "hex",, PrgTool)
        PrgParams = ReplaceToolVariables(PrgParams, "hex",, PrgTool)

        #ifdef __FB_WIN32__  'Need to test as WIN32 matches both 32 and 64 bit
            if dir(PrgExe)="" then
                'Temp = "Programmer executable " + PrgExe + " does not exist"
                Temp = Message("ProgrammerNotFound")
                Replace Temp, "%PrgExe%", PrgExe
                LogError Temp
            End if
        #endif

        If ErrorsFound = 0 Then
          IF VBS = 1 THEN PRINT SPC(5);"Calling    : " + PrgExe
          IF VBS = 1 THEN PRINT SPC(5);  "Parameters : " + PrgParams


          Dim As String SaveCurrDir
          SaveCurrDir = CurDir
          If PrgDir <> "" Then ChDir ReplaceToolVariables(PrgDir, "hex")

          ExitValue = Exec(PrgExe, PrgParams)

          'Check for programmer success, should have 0 exit value
          If ExitValue <> 0 And (LCase(PrgExe) <> "none" And LCase(Right(PrgExe, 5)) <> "\none") Then
            Dim Temp As String
            Temp = Message("WarningProgrammerFail")
            Replace Temp, "%status%", Trim(Str(ExitValue))
            LogWarning Temp
          Else
            ExitValue = 0
            ProgEndTime = Timer

            If VBS = 1 Then
              Dim Temp As String
              Temp = Trim(Str(ProgEndTime - AsmEndTime))
              IF LEN(Temp) > 4 Then Temp = Left(Temp, 5)
              PRINT Message("ProgTime") + Temp + Message("CompSecs")
            End If
          End If
          ChDir SaveCurrDir
        End If
      END If
    ProgEndTime = Timer

    'Issue message
  End If

'If mute donation then show messages

  If MuteDonate = 0 then

    Randomize timer
    
    IF Not ErrorsFound THEN
      BEEP
      randomNumber = int(Rnd * (10 - 1) + 1)
      
      Select Case randomNumber
        Case 1,2,3:
      Print
      Print "Enjoying GCBASIC ?"
      Print
      Print "Donate to help support the 2025 operational costs. Donate using https://gcbasic.com/donate/"
      Print "or, visit https://ko-fi.com/gcstudio to donate"
      
        Case 4,5,6:
      Print
      Print "Using GCBASIC ?"
      Print
      Print "Please donate to help support the 2025 operational costs. Donate using https://gcbasic.com/donate/ "
      Print "or, visit https://ko-fi.com/gcstudio to donate"

        Case Else
      Print
      Print "Developing solutions using GCBASIC ?"
      Print
      Print "Donate to help support the operational costs. Donate here https://gcbasic.com/donate/ "
      Print "or, visit https://ko-fi.com/gcstudio to donate"

      End Select  
      SLEEP 2000
      BEEP
    End if
    
  Else

    If ErrorsFound = 0 Then
      Randomize timer
      randomNumber = int(Rnd * (10 - 1) + 1) 
      If randomNumber = 9  Then
        Print 
        Print "Visit https://ko-fi.com/gcstudio/posts to see the latest news on GCSTUDIO and GCBASIC"
        Print
      End If
    End If
  End If  
'Write compilation report

  WriteCompilationReport

 Fin:
'Write errors to file
  WriteErrorLog

  If VBS = 1 Then
    Dim Temp As String
    Temp = Trim(Str(ProgEndTime - StartTime))
    IF LEN(Temp) > 4 Then Temp = Left(Temp, 5)
    PRINT ""
    PRINT Message("TotalTime") + Temp + Message("CompSecs")
  End If

  If DebugTime > 0 Then
    Color 14
    Print "DebugTime:"; DebugTime; " s"
    Color 7
  End If

'End of program and Pause and wait for key at end of compilation

  If PauseAfterCompile Then
    Print
    WaitForKeyOrTimeout
  End If

  Dim Temp As String
  Temp = Message("Success")
  'Print
  Print Temp

 Finishs:
 Close
 End ExitValue


/'
Start of subs in this GCBASIC source file - these are in alpabetic order NOT functional order, chip family specific order
'/
Sub Add18FBanks(CompSub As SubType Pointer)
  Dim As String TempData, First8, VarName
  Dim As Integer PD, ConstFound, FC

  Dim As LinkedListElement Pointer CurrLine

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    TempData = UCase(CurrLine->Value)
    First8 = Left(TempData, 8)
    'Add banking mode where needed
    IF INSTR(First8, "MOVFF") = 0 AND INSTR(First8, "LFSR") = 0 AND INSTR(First8, "RETFIE") = 0 AND INSTR(TempData, ",ACCESS") = 0 AND INSTR(TempData, ", ACCESS") = 0 AND TempData <> "" AND Left(TempData, 1) <> ";" AND ( Instr(TempData,"#DEFINE" ) = 0 ) THEN
      TempData = Trim(TempData)
      VarName = Mid(TempData, INSTR(TempData, " ") + 1)
      TempData = Left(TempData, INSTR(TempData, " ") - 1)
      IF INSTR(TempData, "F") <> 0 THEN
        IF INSTR(VarName, ",") <> 0 THEN VarName = Left(VarName, INSTR(VarName, ",") - 1)
        VarName = Trim(VarName)

        'permit raw ASM through the ASM source when is leading with #DEFINE.  All other cases should be treated as-was
        IF ( INSTR( trim(CurrLine->Value), "#DEFINE" ) <> 1  and Left( ucase(trim( GetMetaData(CurrLine)->OrgLine)), 4 ) <> "ASM " ) then
            'Check if the variable being accessed is a SFR, and add banking mode
            If IsInAccessBank(VarName) Then
              CurrLine->Value = CurrLine->Value + ",ACCESS"
            Else
              CurrLine->Value = CurrLine->Value + ",BANKED"
            End If
        Else
          'permit ASM with the format of asm #define  - used in MPASX
          'pointless assignment - just left for total clarity
          CurrLine->Value = CurrLine->Value
        End if


      END IF
    End If
    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub AddBankCommands(CompSub As SubType Pointer)
  'Scans through program, adds banksel instructions where needed

  'Not used for AVR
  If ModeAVR Then Exit Sub

  Dim As Integer BankMask, Bank, FindVar, BankselNeeded, ReturnAdded
  Dim As String TempData, OtherData, VarInBank, VarName, DestLabel
  Dim As LinkedListElement Pointer CurrLine, LabelList, LabelListPos, LabelLoc, BankList, BankListLoc, PrintLoc
  Dim As LinkedListElement Pointer RealNextLine
  Dim As ProgLineMeta Pointer CurrMeta, DestMeta
  Dim As String LineToken(100)
  Dim As Integer LineTokens, CurrToken, UseMOVFFL
  Dim As SysVarType Pointer FoundSFR
  Dim As VariableListElement Pointer FoundFinalVar

  'Get bank size
  Select Case ChipFamily
    Case 12: BankMask = 32
    Case 14: BankMask = 384
    Case 15: BankMask = 3968 : If ChipRAM >  4095 then BankMask = 8064
    Case 16: BankMask = 65280
  End Select

  'Add return to end of sub
  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    If CurrLine->Next = 0 Then
      LinkedListInsert(CurrLine, " return")
      ReturnAdded = -1
      CurrLine = 0
    Else
      CurrLine = CurrLine->Next
    End If
  Loop

  'Get list of labels and locations in program
  LabelList = GetLabelList(CompSub)

  'Check required bank for each line
  'Also, ensure metadata attached
  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    'Get metadata for line
    CurrMeta = GetMetaData(CurrLine)

    'Check to see what bank line requires
    TempData = CurrLine->Value

    Bank = -1
    VarInBank = ""
    If Left(Trim(TempData), 6) = "return" Then
      Bank = 0
    ElseIf Left(Trim(TempData), 5) = "call " Then
      Bank = 0

    Else
      'If line operates on file registers, check var usage
      OtherData = UCase(Trim(TempData))
      IF INSTR(OtherData, " ") <> 0 THEN OtherData = Left(OtherData, INSTR(OtherData, " ") - 1)
      IF INSTR(OtherData, "F") <> 0 And WholeINSTR(TempData, "lfsr") <> 2 And WholeINSTR(TempData, "movff") <> 2 Then

        'Split line into tokens
        GetTokens(TempData, LineToken(), LineTokens)
        For CurrToken = 1 To LineTokens
          'Check for SFRs outside of Bank 0
          FoundSFR = GetSysVar(UCase(LineToken(CurrToken)))

          If FoundSFR <> 0 Then
            'Found an SFR

            '18F code: check for SFRs outside of access bank
            If ChipFamily = 16 Then
              If Not IsNonBanked(FoundSFR->Location) Then
                Bank = FoundSFR->Location And BankMask
                VarInBank = FoundSFR->Name
              End If

            '16F1 code: Don't need bank selection for 12 core registers on 16F1xxx
            ElseIf ChipFamily = 15 Then
              If (FoundSFR->Location And Not BankMask) >= 12 Then
                Bank = FoundSFR->Location And BankMask
                VarInBank = FoundSFR->Name
              End If

            'Other PIC code: assume always need bank selection (unless dealing with STATUS, PCL, FSR or INDF)
            Else
              Select Case UCase(FoundSFR->Name)
                Case "STATUS", "PCL", "FSR", "INDF", "PCLATH", "INTCON"
                  Bank = -1
                Case Else
                  Bank = FoundSFR->Location And BankMask
                  VarInBank = FoundSFR->Name
              End Select

            End If
            GoTo RequiredBankFound
          End If

          'Check for user vars outside of bank 0
          FoundFinalVar = HashMapGet(FinalVarList, UCase(LineToken(CurrToken)))
          IF FoundFinalVar <> 0 Then
            With *FoundFinalVar
              'Have found a variable, is banking needed?
              IF Not .IsArray AND Not IsRegister(.Name) Then
                Bank = VAL(.Value) AND BankMask
                VarInBank = .Name
                GoTo RequiredBankFound
              End If
            End With
          End If
        Next

        RequiredBankFound:
      End If
    End If

    With *CurrMeta
      .RequiredBank = Bank
      .VarInBank = VarInBank
    End With

    CurrLine = CurrLine->Next
  Loop

  'Record jump to first line
  'Use null pointer to indicate it
  CurrLine = CompSub->CodeStart->Next
  If CurrLine <> 0 Then
    DestMeta = CurrLine->MetaData
    LinkedListInsert(DestMeta->PrevCommands, 0)
  End If

  'Generate graph of connections between lines
  CurrLine = CompSub->CodeStart->Next
  Dim As String CurrLine4, CurrLine5, CurrLine6, CurrLine8
  Do While CurrLine <> 0

    CurrLine4 = Left(CurrLine->Value, 4)
    CurrLine5 = Left(CurrLine->Value, 5)
    CurrLine6 = Left(CurrLine->Value, 6)
    CurrLine8 = Left(CurrLine->Value, 8)
    CurrMeta = CurrLine->MetaData

    'goto/bra
    If CurrLine6 = " goto " Or CurrLine5 = " bra " Then
      'Print CurrLine->Value; " to label"

      'Get label jumped to
      'Get label name
      If CurrLine6 = " goto " Then
        DestLabel = LCase(Trim(Mid(CurrLine->Value, 7)))
      Else
        DestLabel = LCase(Trim(Mid(CurrLine->Value, 6)))
      End If

      'Is this a delay jump?
      TempData = DestLabel
      Do While InStr(TempData, " ") <> 0
        Replace TempData, " ", ""
      Loop
      If TempData = "$+1" Or TempData = "$+2" Then
        'Record jump to next line
        LinkedListInsert(CurrMeta->NextCommands, CurrLine->Next)
        If CurrLine->Next <> 0 Then
          DestMeta = CurrLine->Next->MetaData
          LinkedListInsert(DestMeta->PrevCommands, CurrLine)
        End If

      'Not a delay jump, find destination
      Else
        'Find label name in list
        LabelLoc = 0
        LabelListPos = LabelList->Next
        Do While LabelListPos <> 0
          If LCase(LabelListPos->Value) = DestLabel Then
            LabelLoc = LabelListPos->MetaData
            Exit Do
          End If
          LabelListPos = LabelListPos->Next
        Loop
        'Record jump
        If LabelLoc <> 0 Then
          'Record in list of next locations for current line
          LinkedListInsert(CurrMeta->NextCommands, LabelLoc)
          'Record in list of previous locations for destination line
          DestMeta = LabelLoc->MetaData
          LinkedListInsert(DestMeta->PrevCommands, CurrLine)
        End If

      End If

    'conditional branch
    ElseIf CurrLine4 = " bc " Or CurrLine4 = " bn " Or _
         CurrLine4 = " bz " Or CurrLine5 = " bnc " Or _
         CurrLine5 = " bnn " Or CurrLine5 = " bnz " Or _
         CurrLine5 = " bov " Or CurrLine6 = " bnov " Then
      'Print CurrLine->Value; " to next or label"
      'Show jump to next
      LinkedListInsert(CurrMeta->NextCommands, CurrLine->Next)
      If CurrLine->Next <> 0 Then
        DestMeta = CurrLine->Next->MetaData
        LinkedListInsert(DestMeta->PrevCommands, CurrLine)
      End If

      'Get label jumped to
      'Get label name
      DestLabel = LCase(Trim(Mid(CurrLine->Value, 9)))
      'Find label name in list
      LabelLoc = 0
      LabelListPos = LabelList->Next
      Do While LabelListPos <> 0
        If LCase(LabelListPos->Value) = DestLabel Then
          LabelLoc = LabelListPos->MetaData
          Exit Do
        End If
        LabelListPos = LabelListPos->Next
      Loop
      'Record jump
      If LabelLoc <> 0 Then
        LinkedListInsert(CurrMeta->NextCommands, LabelLoc)
        DestMeta = LabelLoc->MetaData
        LinkedListInsert(DestMeta->PrevCommands, CurrLine)
      End If

    'btfsc/btfss/cpfseq/cpfslt/cpfsgt/decfsz
    ElseIf CurrLine5 = " btfs" Or CurrLine5 = " cpfs" Or _
          CurrLine8 = " decfsz " Or CurrLine8 = " dcfsnz " Or _
          CurrLine8 = " incfsz " Or CurrLine8 = " infsnz " Or _
          CurrLine8 = " tstfsz" Then
      'Print CurrLine->Value; " to next or after next"
      'Show jump to next
      RealNextLine = NextCodeLine(CurrLine)
      LinkedListInsert(CurrMeta->NextCommands, RealNextLine)
      If RealNextLine <> 0 Then
        'Show jump from this
        DestMeta = RealNextLine->MetaData
        LinkedListInsert(DestMeta->PrevCommands, CurrLine)

        'Show jump after next
        RealNextLine = NextCodeLine(RealNextLine)
        LinkedListInsert(CurrMeta->NextCommands, RealNextLine)
        If RealNextLine <> 0 Then
          DestMeta = RealNextLine->MetaData
          LinkedListInsert(DestMeta->PrevCommands, CurrLine)
        End If
      End If

    'Return
    ElseIf Left(CurrLine->Value, 7) = " return" Then
      'Don't add any jump

    'others
    Else
      'Print CurrLine->Value; " to next"
      'Show jump to next
      LinkedListInsert(CurrMeta->NextCommands, CurrLine->Next)
      If CurrLine->Next <> 0 Then
        DestMeta = CurrLine->Next->MetaData
        LinkedListInsert(DestMeta->PrevCommands, CurrLine)
      End If
    End If

    CurrLine = CurrLine->Next
  Loop

  'Print prev/next
  /'
  If UCase(CompSub->Name) = "READAD" Then
    CurrLine = CompSub->CodeStart->Next
    Color 15
    Do While CurrLine <> 0
      Print "Line:"; CurrLine->Value
      CurrMeta = CurrLine->MetaData

      PrintLoc = CurrMeta->PrevCommands->Next
      Do While PrintLoc <> 0
        BankListLoc = PrintLoc->MetaData
        Print "    After:"; BankListLoc->Value
        PrintLoc = PrintLoc->Next
      Loop
      PrintLoc = CurrMeta->NextCommands->Next
      Do While PrintLoc <> 0
        BankListLoc = PrintLoc->MetaData
        Print "    Before:"; BankListLoc->Value
        PrintLoc = PrintLoc->Next
      Loop
      CurrLine = CurrLine->Next
    Loop
    Color 7
  End If
  '/

  'Find locations where current bank may not match required bank
  CurrLine = CompSub->CodeStart->Next
  'FinalBankselNeeded = 0
  Do While CurrLine <> 0

    'Decide if banksel needed
    BankselNeeded = 0
    VarInBank = ""

    CurrMeta = CurrLine->MetaData
    'Need to make sure metadata exists for line, banksels won't have it
    If CurrMeta <> 0 Then
      'Does line require a particular bank?
      If CurrMeta->RequiredBank <> -1 Then

        'Need to check what bank/s chip will be in
        BankList = FindPotentialBanks(CurrLine)

        'If it can only be in correct bank before line, no need for banksel
        If LinkedListSize(BankList) <> 1 Then
          If LinkedListSize(BankList) <> 0 Or CurrMeta->RequiredBank > 0 Then
            BankselNeeded = -1
          End If

        ElseIf BankList->Next->NumVal <> CurrMeta->RequiredBank Then
          BankselNeeded = -1
        End If

        If BankselNeeded Then
          VarInBank = CurrMeta->VarInBank
          If VarInBank = "" Then
            If CurrMeta->RequiredBank = 0 Then
              If ChipFamily = 16 Then
                VarInBank = "0"
              Else
                VarInBank = "STATUS"
              End If
            Else
              Print "Internal error in AddBankCommands"
            End If
          End If
        End If
      End If
    End If

    'Add banksel
    If BankselNeeded Then
      'Prevent BANKSEL being added UserCodeOnlyEnabled at end of program
      If UserCodeOnlyEnabled = 0  Then 
        LinkedListInsert(CurrLine->Prev, " banksel " + VarInBank)
      Else 
        If CurrLine->Next <> 0 Then
          'Must be UserCodeOnlyEnabled=-1, and not end of the user program
          LinkedListInsert(CurrLine->Prev, " banksel " + VarInBank)
        End if
      End If
    End If
    ''Add final banksel, need to return in bank 0
    'If CurrLine->Next = 0 And FinalBankselNeeded Then
    ' LinkedListInsert(CurrLine, " banksel STATUS")
    ' CurrLine = 0
    'End If

    If CurrLine->Next = 0 Then
      'Remove added return
      If ReturnAdded Then
        If CurrLine->Value = " return" Then
          LinkedListDelete(CurrLine)
          CurrLine = 0
        End If
      End If
    End If

    If CurrLine <> 0 Then
      CurrLine = CurrLine->Next
    End If
  Loop

  'On 18F with larger RAM, replace movff with movffl where necessary
  If ChipFamily = 16 And MemSize >= 4096 Then
    CurrLine = CompSub->CodeStart->Next
    Do While CurrLine <> 0
      If Left(CurrLine->Value, 7) = " movff " Then
        'Get variables
        VarName = Trim(Mid(CurrLine->Value, 8))
        LineToken(2) = Trim(Mid(VarName, InStr(VarName, ",") + 1))
        LineToken(1) = Trim(Left(VarName, InStr(VarName, ",") - 1))
        UseMOVFFL = 0

        'Is either above location 4096?
        For CurrToken = 1 To 2
          'Check for system variable
          FoundSFR = GetSysVar(UCase(LineToken(CurrToken)))
          If FoundSFR <> 0 Then
            If FoundSFR->Location > 4096 Then UseMOVFFL = -1
          Else
            'Check for user var
            FoundFinalVar = HashMapGet(FinalVarList, UCase(LineToken(CurrToken)))
            IF FoundFinalVar <> 0 Then
              With *FoundFinalVar
                If Val(.Value) >= 4096 Then UseMOVFFL = -1
              End With
            End If
          End If
        Next

        'Make replacement
        If UseMOVFFL Then
          CurrLine->Value = " movffl " + LineToken(1) + "," + LineToken(2)
        End If
      End If

      CurrLine = CurrLine->Next
    Loop
  End If
  
  End Sub

Sub AddDataBlocks ( ByRef CurrLine As LinkedListElement Pointer, ByRef CurrPage as Integer, ByRef CurrPagePos As Integer, DataBlockCount as Integer, NonChipFamily16DataBlocksNotAdded As Integer)

  '*WRITE DATA* *WRITE DATABLOCK*

  'Only do this once.
  If NonChipFamily16DataBlocksNotAdded = 0 then Exit Sub
  If DataBlockCount > 0 and ModePIC Then CurrLine = LinkedListInsert(CurrLine, "; DATA blocks. DATA blocks are contiguous and may, or may not, overlap page boundary(ies)." )
  If DataBlockCount > 0 and ModeAVR Then CurrLine = LinkedListInsert(CurrLine, "; DATA blocks. DATA blocks are contiguous." )
  Dim As Integer EPDataHeader, EPDataLoc, CurrEPItem, TableAddressState, AVRAddressState, LogWarningCounter = 0, CurrEPTable, OrgPosOffset
  Dim as String ASMInstruction, Prefix, EPTempData

  For CurrEPTable = 1 TO DataTables
    'Examine all tables

    EPTempData = ""

    With DataTable(CurrEPTable)

      If .Items > 0 AND .IsData = -1 and DataBlockCount > 0 Then              
        'Get data. Where .items the number of items and ISDATA table
        OrgPosOffset = 0

        If .Type = "BYTE" Then

          'ORG counter
          OrgPosOffset = OrgPosOffset + ( .Items * 2 )

          If ModePIC Then
            ' determine prefix
            If Instr(UCase(AsmExe), "GCASM") > 0 Then
              ASMInstruction = "de"
              ' GBASIC Assember needs 0x34 as the high byte
              If ChipFamily = 16 Then
                Prefix =""
              Else
                Prefix ="34"
              End If
            ElseIf Instr(UCase(AsmExe), "MPASM") > 0 Then
              ASMInstruction = "de"
              Prefix ="00"
            Else
              ASMInstruction = "db"
              Prefix = ""
            End IF
          Else
              ' All AVR use .db prefix
              ASMInstruction = ".db"
              Prefix = ""
          End If

          StatsUsedProgram = StatsUsedProgram + Int( ( .items / 2 ) + 0.5 )
          'iterate the items
          For CurrEPItem = 1 To .Items
            ' process to create data strng as BYTE
            EPTempData = EPTempData + ", 0x" + Prefix + Right("0"+HEX(.Item(CurrEPItem)),2)                  
          Next
          If ModeAVR Then
            'Add additional data to ensure 'Warning		.cseg .db misalignment - padding zero byte' is resolved
            If ( .Items MOD 2 ) = 1 Then
              EPTempData = EPTempData + ", 0x00 ; .db alignment pad"
            End If
          End If


        Else  ' Therefore, WORD

          'ORG counter
          OrgPosOffset = OrgPosOffset + ( .Items * 1 )
          If ModePIC Then           
            ASMInstruction = "dw"
          Else
            ASMInstruction = ".dw"
          End If

          StatsUsedProgram = StatsUsedProgram + .items

          'iterate the items
          For CurrEPItem = 1 To .Items Step 1
            ' process to check the data strng
            If MODEPIC Then
              If ChipFamily <> 16 Then
                If .Item(CurrEPItem) > 16383 Then
                    LogWarning message("DataBlockExceeds")
                    LogWarningCounter = LogWarningCounter + 1
                    If LogWarningCounter = 11 then
                      LogError message("DataBlockExceedsTooMany")
                    End If 
                End If
              End If
            Else
              ' AVR - is this too big?
              If .Item(CurrEPItem) > 65535 Then
                LogWarning message("DataBlockExceedsAVR")
                LogWarningCounter = LogWarningCounter + 1
                If LogWarningCounter = 11 then
                  LogError message("DataBlockExceedsTooManyAVR")
                End If 
              End If
            End If
            ' process to create data strng as BYTE
            EPTempData = EPTempData + ", 0x" + Right("0000"+HEX(.Item(CurrEPItem)),4)
          Next
        End If   ' End of Word / If..

        ' Tidy DATABlock string
        If Left(EPTempData, 2 ) = ", " Then EPTempData = Mid(EPTempData,3, Len( EPTempData ) ) 
        
        'This test ensure the address of the DATA is ALIGNed to 2 for ChipFamily = 16 
        If (  EPDataLoc/2 <> Int(EPDataLoc/2) and ChipFamily = 16 ) Then
            EPDataLoc = EPDataLoc + 1
        End If

        If HashMapGet(Constants, Trim(.Name) ) = 0 Then
          AddConstant(Trim(.Name), Str(CurrPagePos))
        Else
          LogError "Duplicate DATA label: '" + Ucase(Trim(.Name)) + "'"
        End If

        'Create DATA block label
        If ModePIC Then  
          CurrLine = LinkedListInsert(CurrLine, Trim(.Name)  )
          GetMetaData(Currline)->IsLabel = -1
          CurrLine = LinkedListInsert(CurrLine, ";  Number of items " + str(.items) )
          
        Else
          CurrLine = LinkedListInsert(CurrLine, Trim(.Name)+":"   )
          GetMetaData(Currline)->IsLabel = -1
          CurrLine = LinkedListInsert(CurrLine, ";  Number of items " + str(.items) )
          
        End If

        If trim(EPTempData) <> "" Then 
          'Do not push out empty structure, as this will cause an error in MPASM etc

          EPTempData = EPTempData +","  'add additional delimter to make this routine work as we use the "," in the instrev()

          Dim as Integer FirstDelimiter, PosOfComma, LastValidPostOfComma, WordCounter, PageSize
          Dim as String StringCut, StringOut

          StringCut = EPTempData
          PageSize = ProgMemPage(CurrPage-1).PageSize
          StringOut = ""
          FirstDelimiter = 1
          WordCounter = 0
          
          'print "Address page", currpage, ProgMemPages, CurrPagePos

          Do
            ' Only output width that MPASM can cope with
            PosOfComma = InStr( FirstDelimiter,  StringCut, "," )
            ' Less the Maxsize AND not at end of string AND ( is NOT MPASM and LESS the 120 chars )
            ' Print CurrPagePos + 1 , PosOfComma  
            If PosOfComma <> 0 AND PosOfComma < 120 Then
                'update outstring
                StringOut = Mid ( StringCut, 1, PosOfComma )
                  WordCounter = WordCounter + 1
                'print "s1 = ",stringout +"| "+Str(PosOfComma)+"| "+ hex(CurrPagePos)
                FirstDelimiter = PosOfComma + 1
                LastValidPostOfComma = PosOfComma
            Else
  
                'Current ORG
                If PageSize <> 0 Then
                  CurrLine = LinkedListInsert(CurrLine, " ORG " + Str(CurrPagePos) )
                End if
                'Remove any comma at the end
                If right( Trim(StringOut),1) = "," Then StringOut = Left( StringOut,Len(StringOut)-1)
                
                'Insert into list
                CurrLine = LinkedListInsert(CurrLine, "  " + ASMInstruction + " " + StringOut )

                'Update the CurrPagePos            
                CurrPagePos = CurrPagePos + WordCounter

                'Tidy the string
                StringCut = trim(Mid ( StringCut, LastValidPostOfComma +1 ))

                ' print CurrPagePos, CurrPagePos MOD .MaxSize, wordcounter, PosOfComma, Len(StringCut)
                
                FirstDelimiter = 1
                WordCounter = 0
                ' print CurrPagePos, Int ( CurrPagePos / PageSize ), CurrPage - 1 , Int ( CurrPagePos / PageSize ) <> CurrPage  - 1
                If PageSize <> 0  and CurrPage > 0 Then
                  If Int ( CurrPagePos / PageSize ) <> CurrPage - 1  Then
                    If CurrPage <= ProgMemPages Then
                      'Increment the page - this is required to page control and ORG statements ( later in this method)
                      CurrPage = CurrPage + 1
                      CurrLine = LinkedListInsert(CurrLine,  "; Page Increment ")
                    End if
                  End If
                End If
            End If
          Loop While PosOfComma > 0 And Len(StringCut) <> 0
        End If        
        CurrLine = LinkedListInsert(CurrLine, "		; End of DATA_BLOCK" )
        'move down as we dont need to align on every table
        IF AFISupport = 1 and ModePIC and ChipFamily = 16 Then
          'There should only one table of this type, and, align may not be needed... but, I do not know if this is the only once....
          CurrLine = LinkedListInsert(CurrLine, " ALIGN 2 ; ASM 2-byte boundary alignment.")
        End if

        EPDataLoc += (.Items + 1)
      End IF
    End With

    
    ' Unset .IsData to ensure this is specific table is not handled again
    If DataTable(CurrEPTable).IsData = -1 then 
      DataTable(CurrEPTable).IsData = 0
      DataTable(CurrEPTable).Used = 0
      ' We have processed a DataBlock, so, reduce the counter
      DataBlockCount = DataBlockCount - 1
    End If

  Next
  
  End Sub

Sub AddPageCommands(CompSub As SubType Pointer)

  Dim As LinkedListElement Pointer CurrLine, NextLine
  Dim As Integer CalledSub, CalledSubPage, ThisSubPage, CheckSub, TempDataCount
  Dim As Integer RestorePage, ForceRestorePage, NextCalledSub, NextCalledSubPage
  Dim As String TempData(100), CallTarget, ThisSubName, NextCallTarget
  Dim As SubType Pointer GotoSub

  If CompSub = 0 Then Exit Sub

  ThisSubPage = CompSub->DestPage

  'This is not needed on AVR or PIC18
  If ModeAVR Then Exit Sub
  If ModePIC And ChipFamily = 16 Then Exit Sub

  'Remove any existing page selection commands
  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    'Get metadata for line
    If CurrLine->MetaData <> 0 Then
      'Line is automatic page sel, remove it
      If GetMetaData(CurrLine)->IsAutoPageSel Then
        CurrLine = LinkedListDelete(CurrLine)
      End If
    End If

    CurrLine = CurrLine->Next
  Loop

  'Search subroutine for calls
  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    'Found a call, get destination and destination page
    If Left(CurrLine->Value, 6) = " call " Then
      GetTokens(CurrLine->Value, TempData(), TempDataCount)
      CallTarget = TempData(2)
      ThisSubName = CompSub->Name
      CalledSub = LocationOfSub(CallTarget, "")
      CalledSubPage = Subroutine(CalledSub)->DestPage

      'Check next line for a call, get dest and dest page
      '(Decide if page needs to be restored after this call)
      RestorePage = -1
      'Find next actual code line, ignore comments
      NextLine = CurrLine->Next
      Do While (Left(NextLine->Value, 1) = ";" Or Left(NextLine->Value, 8) = "PRESERVE") AND NextLine->Next <> 0
        NextLine = NextLine->Next
      Loop
      If NextLine <> 0 Then
        If Left(NextLine->Value, 6) = " call " Then
          'Next line is a call, find the Sub it is calling and what page it is on
          GetTokens(NextLine->Value, TempData(), TempDataCount)
          NextCallTarget = TempData(2)
          NextCalledSub = LocationOfSub(NextCallTarget, "")
          NextCalledSubPage = Subroutine(NextCalledSub)->DestPage
          If NextCalledSubPage <> ThisSubPage Then
            RestorePage = 0
          End If
        End If
      End If

      'Is call in different page?
      If CalledSubPage = ThisSubPage Then
        RestorePage = 0
      End If

      'If called Sub ends in goto, restore
      'May have linked list: called Sub > goto Sub > another goto sub
      'Need to check if called sub's goto, or any other goto will change page
      'GotoSub = Subroutine(NextCalledSub)
      GotoSub = Subroutine(CalledSub)
      Do While GotoSub -> HasFinalGoto
        GotoSub = Subroutine(LocationOfSub(GotoSub->FinalGotoDest, ""))
        If CompSub->DestPage <> GotoSub->DestPage Then
          RestorePage = -1
          Exit Do
        End If
      Loop

      'If called Sub is IndCall, may need to restore page
      If UCase(CallTarget) = "INDCALL" Then
        'Any Sub could be called from IndCall. So, if any subs off page 0, restore needed
        For CheckSub = 1 To SBC
          If Subroutine(CheckSub)->Required And Subroutine(CheckSub)->DestPage <> 0 Then
            RestorePage = -1
            Exit For
          End If
        Next
      End If

      'Alter call to correct instruction/s
      If ModePIC Then
        If ChipFamily = 12 Or ChipFamily = 14 Or ChipFamily = 15 Then
          'Remove call temporarily
          CurrLine = LinkedListDelete(CurrLine)
          If AFISupport = 0 Then
              'GCASM - this is the original method
              'Set page if needed
              If CalledSubPage <> ThisSubPage Then
                CurrLine = LinkedListInsert(CurrLine, " pagesel " + CallTarget)
                GetMetaData(CurrLine)->IsAutoPageSel = -1
              End If
              'Add call back
              CurrLine = LinkedListInsert(CurrLine, " call " + CallTarget)
              'Restore PCLATH if needed
              If RestorePage Then
                CurrLine = LinkedListInsert(CurrLine, " pagesel $")
                GetMetaData(CurrLine)->IsAutoPageSel = -1
              End If
          Else
            'PIC-AS mandates use of fcall... and, no matter how clever we are ... fcall is required
            If CalledSubPage <> ThisSubPage Then
              'Add call back as a fcall
               CurrLine = LinkedListInsert(CurrLine, " fcall " + CallTarget)
            Else
              'Add call back
              CurrLine = LinkedListInsert(CurrLine, " call " + CallTarget)
              'Restore PCLATH if needed
              If RestorePage Then
                CurrLine = LinkedListInsert(CurrLine, " pagesel $")
                GetMetaData(CurrLine)->IsAutoPageSel = -1
              End If
            End if
          End if


        End If
      ElseIf ModeZ8 Then
        CurrLine = LinkedListDelete(CurrLine)
        CurrLine = LinkedListInsert(CurrLine, " call " + CallTarget)
      End If

    'Also deal with gotos that jump to other subs
    ElseIf Left(CurrLine->Value, 6) = " goto " And CompSub->HasFinalGoto Then
      GetTokens(CurrLine->Value, TempData(), TempDataCount)

      'Have found goto, make sure it is a jump to another sub
      CallTarget = TempData(2)
      If LCase(CallTarget) = LCase(CompSub->FinalGotoDest) Then
        ThisSubName = CompSub->Name
        CalledSub = LocationOfSub(CallTarget, "")
        If CalledSub <> 0 Then
          'Have found a jump to a sub
          CalledSubPage = Subroutine(CalledSub)->DestPage

          'Alter call to correct instruction/s
          If ModePIC Then
            If ChipFamily = 12 Or ChipFamily = 14 Or ChipFamily = 15 Then
              CurrLine = LinkedListDelete(CurrLine)
              If AFISupport = 0 then
              'GCASM - this is the original method
                If CalledSubPage = ThisSubPage Then
                  CurrLine = LinkedListInsert(CurrLine, " goto " + CallTarget)
                Else
                  CurrLine = LinkedListInsert(CurrLine, " pagesel " + CallTarget)
                  GetMetaData(CurrLine)->IsAutoPageSel = -1
                  CurrLine = LinkedListInsert(CurrLine, " goto " + CallTarget)
                End If
              Else
              'PIC-AS mandates use of ljmp... and, no matter how clever we are ... ljmp is required
                If CalledSubPage = ThisSubPage Then
                  CurrLine = LinkedListInsert(CurrLine, " goto " + CallTarget)
                Else
                  CurrLine = LinkedListInsert(CurrLine, " ljmp " + CallTarget)
                End If
              End if

            End If
          End If
        End If
      End If
    End If


    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub AddMainEndCode
  Dim As LinkedListElement Pointer CurrPos

  'Prevent return being placed at end
  Subroutine(0)->NoReturn = -1

  'Get last location in main sub
  CurrPos = Subroutine(0)->CodeStart
  Do While CurrPos->Next <> 0
    CurrPos = CurrPos->Next
  Loop

  If ModePIC Then
    If UserCodeOnlyEnabled = 0 then
        CurrPos = LinkedListInsert(CurrPos, "BASPROGRAMEND")
        GetMetaData(CurrPos)->IsLabel = -1
        CurrPos = LinkedListInsert(CurrPos, " sleep")
        'CurrPos = LinkedListInsert(CurrPos, " goto $")
        CurrPos = LinkedListInsert(CurrPos, " goto BASPROGRAMEND")
    End If
  ElseIf ModeAVR Then
    CurrPos = LinkedListInsert(CurrPos, "BASPROGRAMEND:")
    CurrPos = LinkedListInsert(CurrPos, " sleep")
    'CurrPos = LinkedListInsert(CurrPos, "ProgEndLoop:")
    'CurrPos = LinkedListInsert(CurrPos, " rjmp ProgEndLoop")
    CurrPos = LinkedListInsert(CurrPos, " rjmp BASPROGRAMEND")
  ElseIf ModeZ8 Then

  Else
    Print "Unsupported family, Main"

  End If
  End Sub

Sub AddMainInitCode
  'Add initialisation code to start of Main routine
  Dim As LinkedListElement Pointer CurrLine, AddPos, InitRoutineFiles
  Dim As SysVarType Pointer TempBit
  Dim As Integer CurrInc, SubLoc, OldVBS
  Dim As String Temp

  Dim As SubType Pointer AutoDirSub

  CurrLine = Subroutine(0)->CodeStart

  'Mark Main Sub as destined for first page
  Subroutine(0)->FirstPage = -1

  'Set up stack (AVR and Z8)
  If ModeAVR Then
    CurrLine = LinkedListInsert(CurrLine, ";Initialise stack")
    If CHIPAVRDX Then
      'AVRDX support
      If (( compilerdebug and cAVRDXDEBUG ) = cAVRDXDEBUG )  Then
        CurrLine = LinkedListInsert(CurrLine, ";AVRDX #0 support")
      End If

      CurrLine = LinkedListInsert(CurrLine, " ldi SysValueCopy,high(RAMEND)")
      CurrLine = LinkedListInsert(CurrLine, " out "+GetSysVarAliasName ("ALIAS_CPU_SPH")+", SysValueCopy")
      CurrLine = LinkedListInsert(CurrLine, " ldi SysValueCopy,low(RAMEND)")
      CurrLine = LinkedListInsert(CurrLine, " out "+GetSysVarAliasName ("ALIAS_CPU_SPL")+", SysValueCopy") 
    Else
      'Pre AVRDX support code - unchanged
      If HasSFR("SPH") Then
        CurrLine = LinkedListInsert(CurrLine, " ldi SysValueCopy,high(RAMEND)")

        CurrLine = LinkedListInsert(CurrLine, " out SPH, SysValueCopy")
      End If
      If HasSFR("SPL") Then
        CurrLine = LinkedListInsert(CurrLine, " ldi SysValueCopy,low(RAMEND)")
        CurrLine = LinkedListInsert(CurrLine, " out SPL, SysValueCopy")
      ElseIf HasSFR("SP") Then
        CurrLine = LinkedListInsert(CurrLine, " ldi SysValueCopy,low(RAMEND)")
        CurrLine = LinkedListInsert(CurrLine, " out SP, SysValueCopy")
      End If
    End If
  ElseIf ModeZ8 Then
    CurrLine = LinkedListInsert(CurrLine, ";Initialise stack")
    CurrLine = LinkedListInsert(CurrLine, " ldx SPH, #HIGH(RAMEND)")
    CurrLine = LinkedListInsert(CurrLine, " ldx SPL, #LOW(RAMEND)")
  End If

  'Call init routines
  If UserCodeOnlyEnabled = 0 then
      CurrLine = LinkedListInsert(CurrLine, ";Call initialisation routines")
  End If
  InitRoutineFiles = LinkedListCreate
  FOR CurrInc = 1 TO SourceFiles
    With SourceFile(CurrInc)
      If .InitSub <> "" And .InitSubUsed Then
        'Need init sub, add to list
        AddPos = InitRoutineFiles
        Do
          If AddPos->Next = 0 OrElse .InitSubPriority < AddPos->Next->NumVal Then
            AddPos = LinkedListInsert(AddPos, .InitSub)
            AddPos->NumVal = .InitSubPriority
            Exit Do
          End If
          AddPos = AddPos->Next
        Loop While AddPos <> 0

      End If
    End With
  Next

  AddPos = InitRoutineFiles->Next
  Do While AddPos <> 0
    SubLoc = LocationOfSub(AddPos->Value, "")
    If ModeAVR Then
      CurrLine = LinkedListInsert(CurrLine, " rcall " + AddPos->Value)
    Else
      CurrLine = LinkedListInsert(CurrLine, " call " + AddPos->Value)
    End If

    AddPos = AddPos->Next
  Loop
  LinkedListDeleteList(InitRoutineFiles, 0)

  'Enable interrupts
  If UserInt Or SysInt Then
    CurrLine = LinkedListInsert(CurrLine, ";Enable interrupts")
    If ModePIC Then
      TempBit = HashMapGet(SysVarBits, "GIE")
      If TempBit <> 0 Then
        CurrLine = LinkedListInsert(CurrLine, " bsf " + TempBit->Parent + ",GIE")
      End If
      TempBit = HashMapGet(SysVarBits, "PEIE")
      If TempBit <> 0 Then
        CurrLine = LinkedListInsert(CurrLine, " bsf " + TempBit->Parent + ",PEIE")
      End If
    ElseIf ModeAVR Then
      CurrLine = LinkedListInsert(CurrLine, " sei")

    ElseIf ModeZ8 Then
      CurrLine = LinkedListInsert(CurrLine, " ei")

    End If

    'Ensure interrupts stay on - need int state save set when interrupts enabled in main
    If Subroutine(0)->IntStateSaveVar <> "" Then
      CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("1", Subroutine(0)->IntStateSaveVar, "S0"))
    End If
  End If

  'Automatic pin direction setting
  'Create new subroutine to store dir commands
  'Add commands, compile sub, inline code, delete sub
  AutoDirSub = NewSubroutine("SysInitPinDirs")
  LinkedListInsertList(AutoDirSub->CodeStart, GenerateAutoPinDir)
  'If code generated, add it
  If AutoDirSub->CodeStart->Next <> 0 Then
    'Disable verbose mode to stop any output from CompileSubroutine
    OldVBS = VBS: VBS = 0
    'Compile auto pin dir sub
    CompileSubroutine(AutoDirSub)
    VBS = OldVBS
    'Add code from it to main
    CurrLine = LinkedListInsert(CurrLine, ";Automatic pin direction setting")
    CurrLine = LinkedListInsertList(CurrLine, AutoDirSub->CodeStart)
  End If

  'Comment start of main program
  CurrLine = LinkedListInsert(CurrLine, "")
  CurrLine = LinkedListInsert(CurrLine, ";Start_of_the_main_program")
  
  End Sub

Sub AddInterruptCode
  Dim As Integer IntSubLoc, CurrVect
  Dim As LinkedListElement Pointer CurrLine, SubStart, SubEnd

  'If no interrupts are used, no need for any of this
  If Not (UserInt Or SysInt) Then Return

  'Get list of variables to save/restore
  'Need to check every handler to see what registers it uses
  Dim As LinkedListElement Pointer HandlerSubs, SaveVars, CurrCalled, SaveVarPos
  Dim As LinkedListElement Pointer SearchA, SearchB
  Dim As SubType Pointer HandlerSub, CalledSub
  Dim As String RegItem(100), TempData(20), Temp
  Dim As Integer HandlerID, RegItems, CurrReg, SV, CurrBit, DataCount, PCHUsed

  'Update list of Sub calls
  RetrySubRequests

  'Make list of all variables that may need saving
  RegItems = 0
  SaveVars = 0
  If AutoContextSave Then
    RegItems += 1: RegItem(RegItems) = "DelayTemp"
    RegItems += 1: RegItem(RegItems) = "DelayTemp2"
    RegItems += 1: RegItem(RegItems) = "SysDivMultX"
    RegItems += 1: RegItem(RegItems) = "SysWaitTempMS"
    RegItems += 1: RegItem(RegItems) = "SysStringB"
    RegItems += 1: RegItem(RegItems) = "SysDivMultX_H"
    RegItems += 1: RegItem(RegItems) = "SysWaitTempMS_H"
    RegItems += 1: RegItem(RegItems) = "SysStringB_H"
    RegItems += 1: RegItem(RegItems) = "SysDivLoop"
    RegItems += 1: RegItem(RegItems) = "SysWaitTemp10MS"
    RegItems += 1: RegItem(RegItems) = "SysWaitTempS"
    RegItems += 1: RegItem(RegItems) = "SysWaitTempUS"
    RegItems += 1: RegItem(RegItems) = "SysWaitTemp10US"
    RegItems += 1: RegItem(RegItems) = "SysWaitTempM"
    RegItems += 1: RegItem(RegItems) = "SysStringLength"
    RegItems += 1: RegItem(RegItems) = "SysWaitTempUS_H"
    RegItems += 1: RegItem(RegItems) = "SysWaitTempH"
    RegItems += 1: RegItem(RegItems) = "SysDivMultA"
    RegItems += 1: RegItem(RegItems) = "SysStringA"
    RegItems += 1: RegItem(RegItems) = "SysDivMultA_H"
    RegItems += 1: RegItem(RegItems) = "SysStringA_H"
    RegItems += 1: RegItem(RegItems) = "SysDivMultB"
    RegItems += 1: RegItem(RegItems) = "SysReadA"
    RegItems += 1: RegItem(RegItems) = "SysDivMultB_H"
    RegItems += 1: RegItem(RegItems) = "SysReadA_H"
    RegItems += 1: RegItem(RegItems) = "SysSignByte"

    RegItems += 1: RegItem(RegItems) = "DataPointer"

    'EEPROM/NVM registers
    If ModePIC Then
      If HasSFR("EEDATA") Then RegItems += 1: RegItem(RegItems) = "EEDATA"
      If HasSFR("EEDAT") Then RegItems += 1: RegItem(RegItems) = "EEDAT"
      If HasSFR("EEDATL") Then RegItems += 1: RegItem(RegItems) = "EEDATL"
      If HasSFR("EEADRH") Then
        RegItems += 1: RegItem(RegItems) = "EEADRH"
        RegItems += 1: RegItem(RegItems) = "EEADRL"
      ElseIf HasSFR("EEADR") Then
        RegItems += 1: RegItem(RegItems) = "EEADR"
      End If
      If HasSFR("NVMCON1") Then RegItems += 1: RegItem(RegItems) = "NVMCON1"
      If HasSFR("NVMADRU") Then RegItems += 1: RegItem(RegItems) = "NVMADRU"
      If HasSFR("NVMADRH") Then RegItems += 1: RegItem(RegItems) = "NVMADRH"
      If HasSFR("NVMADRL") Then RegItems += 1: RegItem(RegItems) = "NVMADRL"
      If HasSFR("NVMDAT") Then RegItems += 1: RegItem(RegItems) = "NVMDAT"
      If HasSFR("NVMCON1") Then RegItems += 1: RegItem(RegItems) = "NVMCON1"

    ElseIf ModeAVR Then
      RegItems += 1: RegItem(RegItems) = "EEDR"
      If HasSFR("EEARH") Then
        RegItems += 1: RegItem(RegItems) = "EEARH"
        RegItems += 1: RegItem(RegItems) = "EEARL"
      Else
        RegItems += 1: RegItem(RegItems) = "EEAR"
      End If
    End If

    'Temporary calculation registers
    'Need to do A, B, X for SysCalcTemp, SysByteTemp, SysWordTemp, SysIntTemp, SysLongTemp
    Dim TempVarLetter(3) As String = {"A", "B", "X"}
    Dim TempVarType(5) As String = {"Calc", "Byte", "Word", "Integer", "Long"}
    Dim As Integer CurrLetter, CurrTypeNo, CurrByte
    Dim As String CurrType, SaveRegName
    For CurrLetter = 0 To 2
      For CurrTypeNo = 0 To 4
        CurrType = TempVarType(CurrTypeNo)
        If CurrType = "Calc" Then CurrType = "Long"
        For CurrByte = 1 To GetTypeSize(CurrType)
          RegItems += 1: RegItem(RegItems) = GetByte("Sys" + TempVarType(CurrTypeNo) + "Temp" + TempVarLetter(CurrLetter), CurrByte - 1)
        Next
      Next
    Next

    If ModePIC Then
      RegItems += 1: RegItem(RegItems) = "SysIFTemp"

      If ChipFamily = 12 Or ChipFamily = 14 Then
        RegItems += 1: RegItem(RegItems) = "FSR"
        RegItems += 1: RegItem(RegItems) = "PCLATH"

      ElseIf ChipFamily = 16 And ChipFamilyVariant <> 1 Then
        '18FxxK42 chips are family variant 1 and automatically save these
        RegItems += 1: RegItem(RegItems) = "PCLATH"
        RegItems += 1: RegItem(RegItems) = "FSR0"
        RegItems += 1: RegItem(RegItems) = "FSR0H"
        RegItems += 1: RegItem(RegItems) = "FSR1"
        RegItems += 1: RegItem(RegItems) = "FSR1H"
      End If
    End If

    'Add calc vars to list
    For SV = 1 to TCVC
      With CalcVars(SV)
        If .MaxType <> "" Then
          For CurrBit = 0 To GetTypeSize(.MaxType) - 1
            RegItems += 1: RegItem(RegItems) = GetByte("SysTemp" + Str(SV), CurrBit)
          Next
        End If
      End With
    Next

    'Get list for all vectors
    SaveVars = LinkedListCreate
    SaveVarPos = SaveVars

    'Search all handlers for registers
    'When CurrVect = 0, check for INTERRUPT sub
    For CurrVect = 0 To IntCount
      HandlerID = 0
      If CurrVect = 0 Then
        HandlerID = LocationOfSub("INTERRUPT", "")   'was capitalised
      Else
        With Interrupts(CurrVect)
          If .Handler <> "" Then
            'Handler is defined for vector
            HandlerID = LocationOfSub(.Handler, "", "")
          End If
        End With
      End If

      If HandlerID > 0 Then
        'Get all subroutines called from handler
        HandlerSub = Subroutine(HandlerID)
        HandlerSubs = GetCalledSubs(HandlerSub)
        LinkedListInsert(HandlerSubs, HandlerSub)
        'Scan through all handler subs
        CurrCalled = HandlerSubs->Next
        Do While CurrCalled <> 0
          CalledSub = CurrCalled->MetaData
          'Search Sub lines
          CurrLine = CalledSub->CodeStart->Next
          Do While CurrLine <> 0
            'Search line for variables to back up
            CurrReg = 1
            Do While CurrReg <= RegItems
              If WholeINSTR(CurrLine->Value, RegItem(CurrReg)) = 2 Then
                SaveVarPos = LinkedListInsert(SaveVarPos, RegItem(CurrReg))
                'Remove register from list, no need to keep looking for it
                RegItem(CurrReg) = RegItem(RegItems)
                RegItems -= 1
                CurrReg -= 1
              End If
              CurrReg += 1
            Loop

            'Some PIC only values that will need saving
            If ModePIC Then
              If WholeINSTR(CurrLine->Value, "lfsr") <> 0 Then
                GetTokens(CurrLine->Value, TempData(), DataCount)
                If LCase(TempData(1)) = "lfsr" Then
                  SaveVarPos = LinkedListInsert(SaveVarPos, "FSR" + TempData(2) + "L")
                  SaveVarPos = LinkedListInsert(SaveVarPos, "FSR" + TempData(2) + "H")
                End If
              ElseIf WholeINSTR(CurrLine->Value, "AFSR") = 1 Then
                Temp = Mid(CurrLine->Value, InStr(UCase(CurrLine->Value), "AFSR"))
                If Mid(Temp, 7, 1) = "H" Then
                  Temp = "FSR" + Mid(Temp, 5, 1) + Mid(Temp, 7, 1)
                Else
                  Temp = "FSR" + Mid(Temp, 5, 1) + "L"
                End If
                SaveVarPos = LinkedListInsert(SaveVarPos, Temp)
              End If
            End If

            CurrLine = CurrLine->Next
          Loop
          CurrCalled = CurrCalled->Next
        Loop

      End If
    Next

    'Always save PCLATH
    PCHUsed = 0
    If ChipFamily = 14 Or ChipFamily = 15 Then
      If HasSFR("PCLATH") Then
        SaveVarPos = LinkedListInsert(SaveVarPos, "PCLATH")
        PCHUsed = -1
      End If
    End If

    'Delete variables that share location with another saved var
    SearchA = SaveVars->Next
    Do While SearchA <> 0
      SearchB = SearchA->Next
      Do While SearchB <> 0
        'A and B share location, remove B
        If GetRegisterLoc(SearchA->Value) = GetRegisterLoc(SearchB->Value) Then
          If (Left(SearchA->Value, 7) <> "SysTemp" And GetRegisterLoc(SearchA->Value) <> -1) Or _
            LCase(SearchA->Value) = LCase(SearchB->Value) Then
            SearchB = LinkedListDelete(SearchB)
          End If
        End If

        SearchB = SearchB->Next
      Loop
      SearchA = SearchA->Next
    Loop
    'Should now have a list of variables that need to be backed up before interrupt
  End If

  If ModePIC Then
    'On PIC, need to add context save and On Int to start of interrupt routine
    'and add context restore at the end

    'Get interrupt sub
    IntSubLoc = LocationOfSub("INTERRUPT", "")   'was capitalised
    If IntSubLoc = 0 Then
      SBC += 1
      Subroutine(SBC) = NewSubroutine("INTERRUPT")   'was capitalised
      IntSubLoc = SBC
      'Mark as required and compiled (then only insert asm, no BASIC allowed)
      With *Subroutine(SBC)
        '.Required = -1
        .Compiled = -1
      End With
    End If
    With *Subroutine(IntSubLoc)
      'Interrupt Sub must go on first page
      .FirstPage = -1
      'Interrupt Sub doesn't need return added
      .NoReturn = -1
      'Subroutine is required
      .Required = -1

      'Get code start
      SubStart = .CodeStart
    End With
    CurrLine = SubStart

    'Add context save code
    If AutoContextSave Then
      AddVar "SysW", "BYTE", 1, 0, "REAL", "", , -1
      AddVar "SysSTATUS", "BYTE", 1, 0, "REAL", "", , -1

      'Variables to store registers
      SaveVarPos = SaveVars->Next
      Do While SaveVarPos <> 0
        AddVar("Save" + SaveVarPos->Value, "BYTE", 1, 0, "REAL", "", , -1)
        SaveVarPos = SaveVarPos->Next
      Loop
      If ChipFamily = 14 Or ChipFamily = 15 Then
        'Will need to put SysW, SysSTATUS into shared bank
        CurrLine = LinkedListInsert(CurrLine, ";Save Context")
        CurrLine = LinkedListInsert(CurrLine, " movwf SysW")
        CurrLine = LinkedListInsert(CurrLine, " swapf STATUS,W")
        CurrLine = LinkedListInsert(CurrLine, " movwf SysSTATUS")
        If ChipFamily = 15 Then
          AddVar "SysBSR", "BYTE", 1, 0, "REAL", "", , -1
          CurrLine = LinkedListInsert(CurrLine, " movf BSR,W")
          CurrLine = LinkedListInsert(CurrLine, " banksel STATUS")
          CurrLine = LinkedListInsert(CurrLine, " movwf SysBSR")
        Else
          CurrLine = LinkedListInsert(CurrLine, " banksel STATUS")
        End If
        CurrLine = LinkedListInsert(CurrLine, ";Store system variables")
        SaveVarPos = SaveVars->Next
        Do While SaveVarPos <> 0
          CurrLine = LinkedListInsert(CurrLine, " movf " + SaveVarPos->Value + ",W")
          CurrLine = LinkedListInsert(CurrLine, " movwf Save" + SaveVarPos->Value)
          SaveVarPos = SaveVarPos->Next
        Loop
        If PCHUsed Then
          CurrLine = LinkedListInsert(CurrLine, " clrf PCLATH")
        End If
      ElseIf ChipFamily = 16 Then
        AddVar "SysBSR", "BYTE", 1, 0, "REAL", "", , -1

        If ChipFamilyVariant = 1 then
          CurrLine = LinkedListInsert(CurrLine, ";Use Automatic Context Save for K42 and K83 with MVECEN = OFF.  Interrupt priority not supported")
        Else
          CurrLine = LinkedListInsert(CurrLine, ";Save Context")
          CurrLine = LinkedListInsert(CurrLine, " movff WREG,SysW")
          CurrLine = LinkedListInsert(CurrLine, " movff STATUS,SysSTATUS")
          CurrLine = LinkedListInsert(CurrLine, " movff BSR,SysBSR")
        End If
        CurrLine = LinkedListInsert(CurrLine, ";Store system variables")
        SaveVarPos = SaveVars->Next
        Do While SaveVarPos <> 0
          CurrLine = LinkedListInsert(CurrLine, " movff " + SaveVarPos->Value + ",Save" + SaveVarPos->Value)
          SaveVarPos = SaveVarPos->Next
        Loop
      End If
    End If

    'Add On Interrupt generated code
    CurrLine = LinkedListInsert(CurrLine, ";On Interrupt handlers")
    CurrLine = LinkedListInsertList(CurrLine, IntHandlerCode->CodeList)
    CurrLine = LinkedListInsert(CurrLine, ";User Interrupt routine")

    'Get last line in routine
    Do While CurrLine <> 0
      SubEnd = CurrLine
      CurrLine = CurrLine->Next
    Loop

    'Add context restore code
    CurrLine = SubEnd
    CurrLine = LinkedListInsert(CurrLine, "INTERRUPTDONE")  'PIC
    GetMetaData(Currline)->IsLabel = -1

    If AutoContextSave Then
      CurrLine = LinkedListInsert(CurrLine, ";Restore Context")
      If ChipFamily = 14 Or ChipFamily = 15 Then
        CurrLine = LinkedListInsert(CurrLine, ";Restore system variables")
        SaveVarPos = SaveVars->Next
        Do While SaveVarPos <> 0
          CurrLine = LinkedListInsert(CurrLine, " movf Save" + SaveVarPos->Value + ",W")
          CurrLine = LinkedListInsert(CurrLine, " movwf " + SaveVarPos->Value)
          SaveVarPos = SaveVarPos->Next
        Loop
        If ChipFamily = 15 Then
          AddVar "SysBSR", "BYTE", 1, 0, "REAL", ""
          CurrLine = LinkedListInsert(CurrLine, " movf SysBSR,W")
          CurrLine = LinkedListInsert(CurrLine, " movwf BSR")
        End If
        CurrLine = LinkedListInsert(CurrLine, " swapf SysSTATUS,W")
        CurrLine = LinkedListInsert(CurrLine, " movwf STATUS")
        CurrLine = LinkedListInsert(CurrLine, " swapf SysW,F")
        CurrLine = LinkedListInsert(CurrLine, " swapf SysW,W")
        CurrLine = LinkedListInsert(CurrLine, " retfie")
      ElseIf ChipFamily = 16 Then
        CurrLine = LinkedListInsert(CurrLine, ";Restore system variables")
        SaveVarPos = SaveVars->Next
        Do While SaveVarPos <> 0
          CurrLine = LinkedListInsert(CurrLine, " movff Save" + SaveVarPos->Value + "," + SaveVarPos->Value)
          SaveVarPos = SaveVarPos->Next
        Loop

        If ChipFamilyVariant = 1 then
          CurrLine = LinkedListInsert(CurrLine, " retfie 1")
        else
          CurrLine = LinkedListInsert(CurrLine, " movff SysW,WREG")
          CurrLine = LinkedListInsert(CurrLine, " movff SysSTATUS,STATUS")
          CurrLine = LinkedListInsert(CurrLine, " movff SysBSR,BSR")
          CurrLine = LinkedListInsert(CurrLine, " retfie 0")
        end if
      End If

    Else
      'Do not save context, return
      If ChipFamily = 14 Or ChipFamily = 15 Then
        CurrLine = LinkedListInsert(CurrLine, " retfie")
      ElseIf ChipFamily = 16 Then
        CurrLine = LinkedListInsert(CurrLine, " retfie 0")
      End If
    End If

  ElseIf ModeAVR Then
    'On AVR, need to add subs for context save/restore, and set up vectors to
    'call correct handlers

    If AutoContextSave Then
      'Add variables
      AddVar("SaveSysValueCopy", "BYTE", 1, 0, "REAL", "", , -1)
      AddVar("SaveSREG", "BYTE", 1, 0, "REAL", "", , -1)

      'Variables to store registers
      SaveVarPos = SaveVars->Next
      Do While SaveVarPos <> 0
        AddVar("Save" + SaveVarPos->Value, "BYTE", 1, 0, "REAL", "", , -1)
        SaveVarPos = SaveVarPos->Next
      Loop

      'Interrupt subs must go on first page
      'Create context save sub
      SBC += 1: Subroutine(SBC) = NewSubroutine("SysIntContextSave")
      'Set up flags (required, not compiled, needs first page, needs return)
      With *Subroutine(SBC)
        .Required = -1
        .Compiled = 0
        .FirstPage = -1
        .NoReturn = 0
        SubStart = .CodeStart
      End With
      CurrLine = SubStart
      'Add context save code
      CurrLine = LinkedListInsert(CurrLine, ";Store SysValueCopy")
      CurrLine = LinkedListInsert(CurrLine, " sts SaveSysValueCopy,SysValueCopy")
      CurrLine = LinkedListInsert(CurrLine, ";Store SREG")
      
      If ChipAVRDX Then
        CurrLine = LinkedListInsert(CurrLine, " in SysValueCopy,CPU_SREG")
      Else
        CurrLine = LinkedListInsert(CurrLine, " in SysValueCopy,SREG")
      End If

      CurrLine = LinkedListInsert(CurrLine, " sts SaveSREG,SysValueCopy")
      'Store all registers
      CurrLine = LinkedListInsert(CurrLine, ";Store registers")
      SaveVarPos = SaveVars->Next
      Do While SaveVarPos <> 0
        CurrLine = LinkedListInsert(CurrLine, " sts Save" + SaveVarPos->Value + "," + SaveVarPos->Value)
        SaveVarPos = SaveVarPos->Next
      Loop
      CompileSubroutine(Subroutine(SBC))

      'Create context restore sub
      SBC += 1: Subroutine(SBC) = NewSubroutine("SysIntContextRestore")
      'Set up flags (required, not compiled, needs first page, no return)
      With *Subroutine(SBC)
        .Required = -1
        .Compiled = 0
        .FirstPage = -1
        .NoReturn = -1
        SubStart = .CodeStart
      End With
      CurrLine = SubStart
      'Add context restore code
      'Restore all registers
      CurrLine = LinkedListInsert(CurrLine, ";Restore registers")
      SaveVarPos = SaveVars->Next
      Do While SaveVarPos <> 0
        CurrLine = LinkedListInsert(CurrLine, " lds " + SaveVarPos->Value + ",Save" + SaveVarPos->Value)
        SaveVarPos = SaveVarPos->Next
      Loop
      CurrLine = LinkedListInsert(CurrLine, ";Restore SREG")
      CurrLine = LinkedListInsert(CurrLine, " lds SysValueCopy,SaveSREG")
      
      If ChipAVRDX Then
        CurrLine = LinkedListInsert(CurrLine, " out CPU_SREG,SysValueCopy")
      Else
        CurrLine = LinkedListInsert(CurrLine, " out SREG,SysValueCopy")
      End If


      CurrLine = LinkedListInsert(CurrLine, ";Restore SysValueCopy")
      CurrLine = LinkedListInsert(CurrLine, " lds SysValueCopy,SaveSysValueCopy")
      CurrLine = LinkedListInsert(CurrLine, " reti")
      'Compile the sub
      CompileSubroutine(Subroutine(SBC))
    End If

    'Create subs for On Interrupt events
    For CurrVect = 1 To IntCount
      With Interrupts(CurrVect)
        If .Handler <> "" Then
          SBC += 1: Subroutine(SBC) = NewSubroutine("Int" + UCase(.Vector))
          Subroutine(SBC)->Compiled = -1
          Subroutine(SBC)->FirstPage = -1
          Subroutine(SBC)->Required = -1
          Subroutine(SBC)->NoReturn = -1
          CurrLine = Subroutine(SBC)->CodeStart

          If AutoContextSave Then CurrLine = LinkedListInsert(CurrLine, " rcall SysIntContextSave")
          CurrLine = LinkedListInsert(CurrLine, " rcall " + .Handler)
          If .FlagBit <> "" Then

            'AVRDx resolve setting all BITS
            IF CHIPAVRDX Then
              Dim tempbit as String
              Dim bitvalue as Integer = 0
              Dim as Integer bitcounter, bitsset 

              bitsset = 0
              tempbit = ucase(mid(.FlagBit, instr(.FlagBit, ".")+1))
              If Instr( tempbit, "0X" ) > 0 then replace ( tempbit, "0X","&H")
              bitvalue = val(tempbit)

              For bitcounter = 0 to 7
                  If Bit( bitvalue, bitcounter ) = -1 then bitsset = bitsset + 1
              Next
              
              If bitsset = 0  then  
                'So we know this is an AVR BIT must be a register as there is ONE one bit
                If Left(.FlagBit,1)="!" Then 
                  CurrLine = LinkedListInsertList(CurrLine, GenerateBitSet(Mid(.FlagBit,2), "1", ""))
                Else
                  ' else set the AVR register
                  CurrLine = LinkedListInsertList(CurrLine, GenerateBitSet(.FlagBit, "0", ""))
                End If
              Else
                ' set the register value            
                CurrLine = LinkedListInsert(CurrLine, " ldi	SysValueCopy," + str(bitvalue))
                CurrLine = LinkedListInsert(CurrLine, " sts " + left(.FlagBit, instr(.FlagBit, ".")-1) + ",SysValueCopy" )
              End If
            Else
              If Left(.FlagBit,1)="!" Then 
                CurrLine = LinkedListInsertList(CurrLine, GenerateBitSet(Mid(.FlagBit,2), "1", ""))
              Else
                CurrLine = LinkedListInsertList(CurrLine, GenerateBitSet(.FlagBit, "0", ""))
              End If
            End if
          End If
          If AutoContextSave Then
            CurrLine = LinkedListInsert(CurrLine," rjmp SysIntContextRestore")
          Else
            CurrLine = LinkedListInsert(CurrLine, " reti")
          End If
        End If
      End With
    Next

    'Add appropriate return and context save and restore to user Interrupt routine
    IntSubLoc = LocationOfSub("INTERRUPT", "")   'was capitalised
    If IntSubLoc <> 0 Then
      'No return needed
      Subroutine(IntSubLoc)->NoReturn = -1

      'Add save
      CurrLine = Subroutine(IntSubLoc)->CodeStart
      If AutoContextSave Then CurrLine = LinkedListInsert(CurrLine, " rcall SysIntContextSave")

      'Add context restore code
      Do While CurrLine <> 0
        SubEnd = CurrLine
        CurrLine = CurrLine->Next
      Loop
      CurrLine = SubEnd
      CurrLine = LinkedListInsert(CurrLine, "INTERRUPTDONE:")  'AVR
      If AutoContextSave Then
        CurrLine = LinkedListInsert(CurrLine," rjmp SysIntContextRestore")
      Else
        CurrLine = LinkedListInsert(CurrLine, " reti")
      End If

    End If

  ElseIf ModeZ8 Then
    'Z8 can use vectors, so same as AVR

  End If

  'Delete save list
  If SaveVars <> 0 Then
    LinkedListDeleteList(SaveVars, 0)
  End If
  
  End Sub

Sub AddSysVarBits (CompSub As SubType Pointer)
  Dim As String TempData, BitName
  Dim As Integer PD, T, SV, LineChanged

  Dim As LinkedListElement Pointer CurrLine
  Dim As LinkedListElement Pointer LineElements, CurrElement
  Dim As SysVarType Pointer CurrVar

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    TempData = CurrLine->Value
    IF INSTR(TempData, ".") <> 0 And InStr(TempData, "=") = 0 Then GOTO AddNextLineBits
    IF Left(TempData, 1) = " " THEN GOTO AddNextLineBits

    'Only use for some commands
    T = 0
    IF Left(TempData, 4) = "SET " THEN T = 1
    IF Left(TempData, 4) = "DIR " THEN T = 1
    IF Left(TempData, 3) = "IF " THEN T = 1
    IF Left(TempData, 3) = "DO " THEN T = 1
    IF Left(TempData, 5) = "LOOP " THEN T = 1
    IF INSTR(TempData, "=") <> 0 THEN T = 1
    IF Left(TempData, 11) = "WAIT UNTIL " THEN T = 1
    IF Left(TempData, 11) = "WAIT WHILE " THEN T = 1
    IF T = 0 THEN GOTO AddNextLineBits

    LineElements = GetElements(TempData, , -1)
    LineChanged = 0
    CurrElement = LineElements->Next
    Do While CurrElement <> 0
      CurrVar = HashMapGet(SysVarBits, CurrElement->Value)
      If CurrVar <> 0 Then
        'Found SFR bit, should have . before it, or else we need to add it
        If CurrElement->Prev->Value <> "." Then
          IF UCase(CurrElement->Value) <> "DIR" AND Left(TempData, 4) <> "DIR " Then
            LinkedListInsert(CurrElement->Prev, CurrVar->Parent + ".")
            LineChanged = -1
          End If
        End If
      End If

      CurrElement = CurrElement->Next
    Loop

    If LineChanged Then
      CurrLine->Value = ""
      CurrElement = LineElements->Next
      Do While CurrElement <> 0
        CurrLine->Value = CurrLine->Value + CurrElement->Value
        CurrElement = CurrElement->Next
      Loop
    End If

    AddNextLineBits:

    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub BuildMemoryMap

  'Dimension arrays
  REDIM FreeMem (MemSize + 10) As Integer
  REDIM VarLoc (MemSize + 10) As Integer
  Dim As String TempData
  Dim As Integer PD, Range, Min, Max, L, T, CheckNonBanked

  'Mark all locations as used
  For PD = 0 To MemSize + 10
    FreeMem(PD) = 1
  Next


  'Mark locations as free if specified by a range
  'Do not mark non-banked locations as free yet
  For Range = 1 to MRC
    TempData = MemRanges(Range)
    Min = VAL("&h" + Left(TempData, INSTR(TempData, ":") - 1))
    Max = VAL("&h" + Mid(TempData, INSTR(TempData, ":") + 1))

    For L = Min To Max
      If DATfileinspection = True Then
        If L > MemSize then
            LogError "DAT file error - probable `MaxAddress = "+ str(MemSize)+"` is incorrect/too small. Check DAT with DATASHEET to correct error"
            exit sub
        End if
      End if
      'On 16F1 chips, keep non-banked locations at end of list to make allocation of linear memory simpler
      If ChipFamily = 15 Then

        For CheckNonBanked = 1 To NoBankLocs
          If L >= NoBankLoc(CheckNonBanked).StartLoc And L <= NoBankLoc(CheckNonBanked).EndLoc Then
            FreeMem(L) = 2
            GoTo LocationIsNonBanked
          End If
        Next
      End If

      FreeMem(L) = 0
      LocationIsNonBanked:
    Next
  Next

  'Produce list of free memory locations
  T = 0
  'Banked locations first

  For PD = 1 To MemSize
    If FreeMem(PD) = 0 Then
      T += 1
      VarLoc(T) = PD
      If T = ChipRam Then Exit For
    End If
  Next
  'Then non-banked locations
  If ChipFamily = 15 Then
    For PD = 1 To MemSize
      If FreeMem(PD) = 2 Then
        T += 1
        VarLoc(T) = PD
        If T = ChipRam Then Exit For
      End If
    Next
  End If
  FreeRAM = T
  
  End Sub

Sub CalcConfig

  Dim As Integer configreport
  Dim As String ConfigReportFileName

  'Process config flags from CONFIG variable and programmer required settings
  'Store required settings in OutConfig array

  'No config registers on AVR that can be set by GCBASIC
  If ModeAVR Then
    If OutPutConfigOptions = 0 then
      configreport = FreeFile
      ConfigReportFileName = ReplaceToolVariables("%filename%", "config")
      Open ConfigReportFileName For Output As #configreport
      Print #configreport, "[" + ChipName + "]"+str(ModePIC)+","+str(ModeAVR)+",,"+str(chipfamily)
      Close #configreport
    End If
    If NOT CHIPAVRDX Then
      Exit Sub
    End If
  End If

  'Do not set CONFIG if TBL+
  If ConfigDisabled Then Exit Sub

  Dim As String CurrItem, CurrName, CurrVal, Temp, LFINTOSCString
  Dim As Integer CurrWord, CurrConfConst, CurrSpeed
  Dim As LinkedListElement Pointer UserSettingList, UserSettingLoc, CodeLoc
  Dim As LinkedListElement Pointer CurrSettingLoc, CurrSettingOptLoc
  Dim As ConfigSetting Pointer CurrSetting

  'Set default - this is needed as some dont use the same LF INT OSC
  LFINTOSCString = "LFINT"


  'AVRDX Support
    'Check for internal osc
    If IntOscSpeeds <> 0 Then
    For CurrSpeed = 1 To IntOscSpeeds
      If CSng(ChipMhz) = CSng(IntOscSpeed(CurrSpeed)) Then
        AddConstant("CHIPUSINGINTOSC", "TRUE")
        Exit For
      End If
    Next
    End If
    If CHIPAVRDX Then 
      Exit Sub
    End If


  'Read config
  Do WHILE INSTR(CONFIG, "&") <> 0: Replace CONFIG, "&", ",": Loop
  'Split line into elements, put into list
  UserSettingList = LinkedListCreate
  UserSettingLoc = UserSettingList
  Do While InStr(Config, ",") <> 0
    UserSettingLoc = LinkedListInsert(UserSettingLoc, Trim(Left(Config, InStr(Config, ",") - 1)))
    Config = Trim(Mid(Config, InStr(Config, ",") + 1))
  Loop
  If Config <> "" Then
    UserSettingLoc = LinkedListInsert(UserSettingLoc, Config)
  End If

  'Add config for programmer
  If PrgTool <> 0 Then
    If PrgTool->ProgConfig <> "" Then
      Config = PrgTool->ProgConfig
      Do While InStr(Config, ",") <> 0
        UserSettingLoc = LinkedListInsert(UserSettingLoc, Trim(Left(Config, InStr(Config, ",") - 1)))
        Config = Trim(Mid(Config, InStr(Config, ",") + 1))
      Loop
      If Config <> "" Then
        UserSettingLoc = LinkedListInsert(UserSettingLoc, Config)
      End If
    End If
  End If

  'Search through list of user entered config settings
  'Then find a match in the list of valid settings for the PIC
  'If no match found, show error

  'Get a user setting
  UserSettingLoc = UserSettingList->Next
  Do While UserSettingLoc <> 0
    'Get a setting name
    CurrSettingLoc = ConfigSettings->Next
    Do While CurrSettingLoc <> 0
      CurrSetting = CurrSettingLoc->MetaData
      With (*CurrSetting)
        'Check for setting name in user setting
        If ConfigNameMatch(UserSettingLoc->Value, .Name) Then
          'Print "Name match for " + .Name
          'If found, find matching setting value
          CurrSettingOptLoc = .Options
          Do While CurrSettingOptLoc <> 0

            If ConfigValueMatch(CurrSettingOptLoc->Value, UserSettingLoc->Value) <> 0 Then
              'Matching setting value found
              'Print "Found match for: "; UserSettingLoc->Value; ", "; .Name; " = "; CurrSettingOptLoc->Value
              .Setting = CurrSettingOptLoc
              If ConfigValueMatch(CurrSettingOptLoc->Value, UserSettingLoc->Value) = -2 Then GoTo ProcessNextSetting
            End If
            CurrSettingOptLoc = CurrSettingOptLoc->Next
          Loop

          If .Setting <> 0 Then GoTo ProcessNextSetting
        End If

      End With
      CurrSettingLoc = CurrSettingLoc->Next
    Loop

    'If setting has been found, will jump to ProcessNextSetting
    'Only way to get here is if a setting hasn't been found
    'Print "Bad setting: " + UserSettingLoc->Value
    Temp = Message("BadConfig")
    Replace Temp, "%option%", UserSettingLoc->Value
    LogWarning(Temp, "")

    ProcessNextSetting:
    UserSettingLoc = UserSettingLoc->Next
  Loop

  'Add default CONFIG options
  'Find settings with nothing specified
  Dim As String DesiredSetting
  'Check here as I could not find better place for it!
  IntOscSpeedValid = 0
  If CSng(ChipMHz) = CSng(0.031) Then
    If _31kSupported.State = -1 Then
      DesiredSetting = _31kSupported.OSCType
      IntOscSpeedValid = 1
    End if
  End If

  CurrSettingLoc = ConfigSettings->Next
  Do While CurrSettingLoc <> 0
    CurrSetting = CurrSettingLoc->MetaData
    With (*CurrSetting)
      If .Setting = 0 Then
        'Get the desired default setting
        DesiredSetting = ""
        If ConfigNameMatch(.Name, "MCLR") Then
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "LVP") Then
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "WDT") Then
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "PLLEN") Then
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "CPUDIV") Then
          DesiredSetting = "NOCLKDIV"
        ElseIf ConfigNameMatch(.Name, "MVECEN") Then
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "FEXTOSC") Then
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "CP") Then
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "CPD") Then
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "CLKOUTEN") Then
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "VCAPEN") Then
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "WRTD") Then
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "XINST") Then
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "WRT") Then   'this was generating a config error in non GCASM implementations
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "FCMEN") Then   'this was generating a config error in non GCASM implementations
          DesiredSetting = "ON"
        ElseIf ConfigNameMatch(.Name, "WRTSAF") Then   'this was generating a config error in non GCASM implementations
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "WRTB") Then     'this was generating a config error in non PICAS implementations
          DesiredSetting = "OFF"
        ElseIf ConfigNameMatch(.Name, "VDDIO2MD") Then     'new for Q20 chips
          DesiredSetting = "STANDARD_RANGE"
        ElseIf ConfigNameMatch(.Name, "VDDIO3MD") Then     'new for Q20 chips
          DesiredSetting = "STANDARD_RANGE"

        ElseIf ConfigNameMatch(.Name, "OSC") Then
          'Get setting from #osc directive

          IF OSCType <> "" Then
            DesiredSetting = OSCType
          Else

            'No oscillator chosen, need to find best option
            'If ChipMhz = IntOscSpeed(x), use Int Osc
            'If ChipMhz > 4, use HS
            'If 4 > ChipMhz > ChipIntOsc, use XT
            'Check for internal osc
            If IntOscSpeeds <> 0 Then
              For CurrSpeed = 1 To IntOscSpeeds
                If CSng(ChipMhz) = CSng(IntOscSpeed(CurrSpeed)) Then
                  DesiredSetting = "INT"
                  Exit For
                End If
              Next
            End If

            If CSng(ChipMHz) = CSng(0.031) Then
              If _31kSupported.State = -1 Then
                DesiredSetting = _31kSupported.OSCType
                IntOscSpeedValid = 1
              End if
            End If

            'Can't use int osc, come up with suitable ext osc
            If DesiredSetting = "" Then
              If ChipMhz > 4 Then
                DesiredSetting = "HS"
              Else
                DesiredSetting = "XT"
              End If
            End If

            ChipOscSource = DesiredSetting
          End If

        End If

        'If there is a default, use it
        If DesiredSetting <> "" Then
          CurrSettingOptLoc = .Options
          Do While CurrSettingOptLoc <> 0      
            'Select correct LFINT.. if this chip is LFINTOSC
            'print CurrSettingOptLoc->Value, WholeINSTR(CurrSettingOptLoc->Value, "LFINTOSC" )
            If WholeINSTR(CurrSettingOptLoc->Value, "LFINTOSC" ) = 2 Then
              If DesiredSetting = "LFINT" then
                DesiredSetting = "LFINTOSC"
              end if
            end if
            If ConfigValueMatch(CurrSettingOptLoc->Value, DesiredSetting) Then
              'Matching setting value found
              .Setting = CurrSettingOptLoc
              If ConfigValueMatch(CurrSettingOptLoc->Value, DesiredSetting) = -2 Then Exit Do
                End If
            CurrSettingOptLoc = CurrSettingOptLoc->Next
          Loop
        End If

      End If
    End With
    CurrSettingLoc = CurrSettingLoc->Next
  Loop

  'Publish config
  If OutPutConfigOptions = 0 then
    configreport = FreeFile
    ConfigReportFileName = ReplaceToolVariables("%filename%", "config")
    Open ConfigReportFileName For Output As #configreport
    IF HasSFR ( "PPSLOCK" ) then
      Print #configreport, "[" + ChipName + "]"+str(ModePIC)+","+str(ModeAVR)+",PPS,"+str(chipfamily)
    else
      Print #configreport, "[" + ChipName + "]"+str(ModePIC)+","+str(ModeAVR)+",,"+str(chipfamily)
    end if
  End If

  'Check oscillator type, add constant
  CurrSettingLoc = ConfigSettings->Next
  Do While CurrSettingLoc <> 0
    CurrSetting = CurrSettingLoc->MetaData
    With (*CurrSetting)
      If ConfigNameMatch(.Name, "OSC") and not ( ConfigNameMatch(.Name, "SOSCSEL") or ConfigNameMatch(.Name, "LPT1OSC")  or ConfigNameMatch(.Name, "INTOSC_XT") ) Then
        ChipOscSource = .Setting->Value
        AddConstant("CHIPOSC", ChipOscSource)
        'Cache the ChipOscSource value @1121
        'The purpose is to support INT OSC with CLOCKOUT - the initsys block of code works for CLOCK and NOCLOCK so, cache the value, treat as NOCLOCK and restore the config setting
        Dim ChipOscSourceCache as String = ""
        If ChipOscSource = "INTRC_OSC_CLKOUT" Then
          ChipOscSourceCache = ChipOscSource
          ChipOscSource = "INTRC_OSC_NOCLKOUT"
        End if
        If ConfigValueMatch(ChipOscSource, "INT", -1) Then
          'Check for internal osc
          If IntOscSpeeds <> 0 Then
            For CurrSpeed = 1 To IntOscSpeeds
              If CSng(ChipMhz) = CSng(IntOscSpeed(CurrSpeed)) Then
                IntOscSpeedValid = 1
                Exit For
              End If
            Next
          End If
          'Restore the cached value @1121
          IF ChipOscSourceCache <> "" Then
            ChipOscSource = ChipOscSourceCache
          End If
          If IntOscSpeedValid = 1 then
            AddConstant("CHIPUSINGINTOSC", "TRUE")
          else
            LogWarning(Message("BadIntFreq"))
          End if
        End If
        Exit Do
      End If
    End With
    CurrSettingLoc = CurrSettingLoc->Next
  Loop


  'Store config
  'PIC 10/12/16 format
  If ChipFamily <> 16 Then

    'Find all settings
    CurrSettingLoc = ConfigSettings->Next
    Do While CurrSettingLoc <> 0
      CurrSetting = CurrSettingLoc->MetaData
      With (*CurrSetting)
        'If a setting has been set, need to write out
        If .Setting <> 0 Then

          'Find where setting came from
          'Print .Name, .Setting->Value
          For CurrConfConst = 1 To COC
            If ConfigNameMatch(ConfigOps(CurrConfConst).OP, .Name) And _
              ConfigValueMatch(ConfigOps(CurrConfConst).OP, .Setting->Value) Then
              'Store setting
              CurrWord = ConfigOps(CurrConfConst).Loc
              If OutConfig(CurrWord) = "" Then
                OutConfig(CurrWord) = "_" + ConfigOps(CurrConfConst).Op
                If OutPutConfigOptions = 0 then
                  Print #configreport, ConfigOps(CurrConfConst).Op
                End if
              Else
                OutConfig(CurrWord) = OutConfig(CurrWord) + " & _" + ConfigOps(CurrConfConst).Op
                If OutPutConfigOptions = 0 then
                  Print #configreport, ConfigOps(CurrConfConst).Op
                End if
              End If

              Exit For
            End If
          Next

        End If
      End With


      CurrSettingLoc = CurrSettingLoc->Next
    Loop

    'Write code
    'Single config word
    If ConfWords = 1 Then
      CodeLoc = LinkedListInsert(ChipConfigCode->CodeList, " __CONFIG " + OutConfig(1))
      GetMetaData(CodeLoc)->RawConfig = OutConfig(1)

    'Multiple config words
    Else
      CodeLoc = ChipConfigCode->CodeList
      For CurrWord = 1 To ConfWords
        'Only write non-blank words
        If OutConfig(CurrWord) <> "" Then
          CodeLoc = LinkedListInsert(CodeLoc, " __CONFIG _CONFIG" + Str(CurrWord) + ", " + OutConfig(CurrWord))
          GetMetaData(CodeLoc)->RawConfig = OutConfig(CurrWord)
        End If
      Next
    End If

  'PIC 18 format
  Else
    OutConfig(1) = ""

    'Use OSC or FOSC?
    Dim As Integer FOSC
    FOSC = 0
    For CurrConfConst = 1 To COC
      With ConfigOps(CurrConfConst)
        If Left(.Op, 5) = "FOSC_" Then
          FOSC = -1
          Exit For
        End If
      End With
    Next

    'Find all settings
    CurrSettingLoc = ConfigSettings->Next
    Do While CurrSettingLoc <> 0
      CurrSetting = CurrSettingLoc->MetaData
      With (*CurrSetting)
        'If a setting has been set, need to write out
        If .Setting <> 0 Then

          If FOSC Then
            If .Name = "OSC" Then .Name = "FOSC"
          End If
          ' as MVECEN is not supported (see Interrupt handler section).
          If ucase(trim(.Name)) = "MVECEN" and ucase(trim(.Setting->Value)) = "ON" Then
              Temp = Message("BadConfig")
              Replace Temp, "%option%", .Name + "=" + .Setting->Value + " not supported by compiler.  Use MVECEN = OFF or remove from config statement"
              LogWarning(Temp, "")
          End If

          If OutConfig(1) = "" Then
            OutConfig(1) = .Name + " = " + .Setting->Value
            If OutPutConfigOptions = 0 then
              Print  #configreport, .Name + " = " + .Setting->Value
            End if
          Else
            OutConfig(1) = OutConfig(1) + ", " + .Name + " = " + .Setting->Value
            If OutPutConfigOptions = 0 then
              Print #configreport, .Name + " = " + .Setting->Value
            End if
          End If
        End If
      End With
      CurrSettingLoc = CurrSettingLoc->Next
    Loop
    LinkedListInsert(ChipConfigCode->CodeList, " CONFIG " + OutConfig(1))

  End If

  If OutPutConfigOptions = 0 then
    Close #configreport
  End if
  
  End Sub

Sub CalcOps (OutList As CodeSection Pointer, SUM As String, AV As String, Ops As String, OriginIn As String, NeverLast As Integer)

  Dim As String Origin, Temp
  Dim As String Answer, ActN, AnswerIn, V1, V2, TypeV1, TypeV2, CalcType, TypeAV, Act
  Dim As Integer UnaryMode, SearchStart, CalcisBad, OpPos, SD, SO
  Dim As Integer CalcStart, CalcEnd, LastCalc, NextSame, PD
  Dim As SubType Pointer CurrentSub, DestSub

  Dim As LinkedListElement Pointer NewCode

  If (( compilerdebug and cCALCOPS ) = cCALCOPS )  Then
    Print " 4 CALCOPS origin: " + OriginIn, SUM, ops, av
  End If
  Origin = OriginIn
  CurrentSub = Subroutine(GetSubID(Origin))
  IF INSTR(Origin, "D") <> 0 Then DestSub = Subroutine(GetDestSub(Origin)) Else DestSub = CurrentSub

  UnaryMode = 0
  If Left(Ops, 1) = "U" Then
    UnaryMode = -1
    Ops = Mid(Ops, 2)
  End If

  SearchStart = 1
  SearchForOpAgain:
  'Initialise
  CalcIsBad = 0
  Answer = ""

  'Find operator
  OpPos = 0
  Act = ""
  FOR SD = SearchStart to LEN(SUM)
    Temp = "": IF SD > 1 Then Temp = Mid(SUM, SD - 1, 1)
    FOR SO = 1 to LEN(Ops)
      If (Not UnaryMode And Mid(SUM, SD, 1) = Mid(Ops, SO, 1)) Or (UnaryMode And Mid(SUM, SD, 1) = Mid(Ops, SO, 1) And IsCalcDivider(Temp)) Then
        Act = Mid(Ops, SO, 1)
        OpPos = SD
        'Exit For
        GoTo FoundOp
      End If
    Next
  Next
  FoundOp:

  'If operator not found, exit sub
  If OpPos = 0 Then Exit Sub

  'Get normal name for action
  ActN = Act
  If ActN = "{" Then ActN = "<="
  If ActN = "}" Then ActN = ">="
  If ActN = "~" Then ActN = "<>"

  'Get left operand
  V1 = ""
  CalcStart = 0
  If OpPos = 1 Or UnaryMode Then
    CalcStart = OpPos
  Else
    For SD = OpPos - 1 to 0 step -1
      If SD = 0 Then
        V1 = Left(SUM, OpPos - 1)
        CalcStart = 1
        Exit For
      End If
      If IsCalcDivider(Mid(SUM, SD, 1)) Then
        If Mid(SUM, SD, 1) <> "-" Or (Not IsCalcDivider(Mid(SUM, SD - 1, 1)) And SD > 1) Then
          V1 = Left(SUM, OpPos - 1)
          V1 = Mid(V1, SD + 1)
          CalcStart = SD + 1
          Exit For
        End If
      End If
    Next
  End If

  'Get right operand
  V2 = ""
  CalcEnd = OpPos
  For SD = OpPos + 2 to (LEN(SUM) + 1)
    If SD > LEN(SUM) Then
      V2 = Mid(SUM, OpPos + 1)
      CalcEnd = LEN(SUM)
      Exit For
    End If
    If IsCalcDivider(Mid(SUM, SD, 1)) Then
      V2 = Left(SUM, SD - 1)
      V2 = Mid(V2, OpPos + 1)
      CalcEnd = SD - 1
      Exit For
    End If
  Next

  'Check syntax
  CalcIsBad = 0
  If V1 = "" And Not UnaryMode Then
    If Act = "-" Then
      'If act is -, may have a negative literal
      SearchStart = OpPos + 1
      GoTo SearchForOpAgain
    Else
      Temp = Message("MissingOperand")
      Replace Temp, "%pos%", "before"
      Replace Temp, "%operator%", ActN
      LogError Temp, Origin
      CalcIsBad = -1
    End If
  End If
  If V2 = "" Then
    Temp = Message("MissingOperand")
    Replace Temp, "%pos%", "after"
    Replace Temp, "%operator%", ActN
    LogError Temp, Origin
    'Print SUM, Origin, Temp
    CalcIsBad = -1
  End If

  'If calc is bad, quit and try next operator
  If CalcIsBad Then
    SUM = Left(SUM, CalcStart - 1) + "SynErr" + Mid(SUM, CalcEnd + 1)
    Goto SearchForOpAgain
  End If

  'Get input types
  TypeV1 = TypeOfValue(V1, Subroutine(GetSubID(Origin)))
  TypeV2 = TypeOfValue(V2, Subroutine(GetSubID(Origin)))
  If UnaryMode Then TypeV1 = ""
  TypeAV = TypeOfValue(AV, Subroutine(GetDestSub(Origin)))

  '[1113]
  'Resolves match =  times_array(1) + 2 >= times_array(3) error
  ' where the times_array(1) + 2 is treated a SYSTEMP byte variable  when the array is a word element array .. thus messes up the ASM and the users code.
  ' the cause of the issue is that the 'TypeAV = TypeOfValue(AV, Subroutine(GetDestSub(Origin)))' above returns a byte not a word
  ' so, this code tests that TypeAV is not empty, when empty then the compiler has not determine a type, so, it is not of interest
  ' then, it test for > or M then this is a calulation that could impact the type cast.
  ' If all these tests are valid, then simply choose the largest of the two variables.


  If TypeAV <> "" Then
    If TypeV1 <> "BYTE" OR TypeV2 <> "BYTE" Then
      IF  ( Instr( SUM, "}" ) > 0  OR  Instr( SUM, "{" ) > 0 ) Then
        If  ( Instr(OPS, "+-") > 0 OR Instr(OPS, "*/%") > 0 ) Then
            dim temp as string
            If CastOrder( TypeV2 ) > CastOrder( TypeV1 ) Then
                TypeAV = TypeV2
            Else
                TypeAV = TypeV1
            End if
            temp = Message("WarningConditionalExpressionAssumesVarType")
            Replace temp,"%vartype%",TypeAV
            'Logwarning temp, Origin
            LinkedListInsert(OutList->CodeEnd, ";"+temp)
        End If
      End If
    End If
  End If

  CalcType = GetCalcType(TypeV1, Act, TypeV2, TypeAV)
  ' Potential debug point
  If (( compilerdebug and cCALCOPS ) = cCALCOPS ) Then
    Print " 4 CALCOPS       : " +V1, Act, V2, CalcType, AV, ": " + TypeV1 + " " + Act + " " +TypeV2, Origin
  End If
  'Decide output variable
  If CalcStart = 1 And CalcEnd = LEN(SUM) And AV <> "" And (Not NeverLast) Then
    AnswerIn = AV
    LastCalc = -1
  Else
    'If CalcType is BIT, get BYTE variable
    If CalcType = "BIT" Then CalcType = "BYTE"
    AnswerIn = GetCalcVar(CalcType)

    If ModeAVR and Instr( UCASE( Origin), ";?F1L" ) > 0 Then  'examine only user program ignoring libraries which are assumed to be safe
      'Ensure Systemp1 is NOT used when complex maths with MUL - causesSystemp1 (R0 and R1 ) being overwritten by the MUL results
      'So, dont use Systemp1 else we will get incorrect maths
      If CountOccur(Sum, "';+-*/%&|#!") > 2 And HMult and  Instr( Ucase(AnswerIn), "SYSTEMP1") Then
        AnswerIn = GetCalcVar(CalcType)
      End if
    End If

    LastCalc = 0
  End If

  'Does next calc use same system vars for result?
  NextSame = 0
  If CountOccur(SUM, "';*/%=~<>{}") > 1 And CountOccur(Act, "';*/%=~<>{}") > 0 Then NextSame = -1
  If NeverLast Then NextSame = -1
  'Print SUM, Act, NextSame

  'Define all vars found as byte
  'AddVar will ignore any constants/system vars/vars already defined
  AddVar DelType(V1), "BYTE", 1, CurrentSub, "REAL", Origin
  AddVar DelType(V2), "BYTE", 1, CurrentSub, "REAL", Origin
  AddVar AnswerIn, "BYTE", 1, DestSub, "REAL", Origin

  Color 3

  'Generate code for unary ops
  If UnaryMode Then
    Answer = CompileCalcUnary(OutList, Act, V2, Origin, AnswerIn)
    SearchStart += 1

  'Generate code for binary ops
  Else
    If Act = "*" Or Act = "/" Or Act = "%" Then Answer = CompileCalcMult(OutList, V1, Act, V2, Origin, AnswerIn)
    If Act = "+" Or Act = "-" Then Answer = CompileCalcAdd(OutList, V1, Act, V2, Origin, AnswerIn)
    If Act = "=" Or Act = "~" Or Act = "<" Or Act = ">" Or Act = "{" Or Act = "}" Then Answer = CompileCalcCondition(OutList, V1, Act, V2, Origin, AnswerIn)
    If Act = "&" Or Act = "|" Or Act = "#" Then Answer = CompileCalcLogic(OutList, V1, Act, V2, Origin, AnswerIn)
    'Note 13/2/2012: This error should never appear. Here just in case something stupid has happened!
    If Act = "!" Then Print "Internal error: Unary operator ! not handled properly"
  End If
  'Print SUM, V1, Act, V2, AnswerIn, Answer

  'Don't bother with NextSame if calc result in const
  If IsConst(Answer) Then NextSame = 0

  'Copy answer if needed
  If Answer <> AnswerIn And (LastCalc Or NextSame) Then
    NewCode = CompileVarSet(Answer, AnswerIn, Origin)
    OutList->CodeEnd = LinkedListInsertList(OutList->CodeEnd, NewCode)
    FreeCalcVar Answer
    Answer = AnswerIn
  End If

  'Release calc vars
  If Answer <> V1 And Not UnaryMode Then FreeCalcVar V1
  If Answer <> V2 Then FreeCalcVar V2
  If Answer <> AnswerIn And Not LastCalc Then FreeCalcVar AnswerIn
  Color 7

  'Store answer in sum
  'Print SUM,
  SUM = Left(SUM, CalcStart - 1) + Answer + Mid(SUM, CalcEnd + 1)
  'Print Answer, SUM

  Goto SearchForOpAgain
  
  End Sub

Function CalcLineSize(CurrLine As String, ThisSubPage As Integer, CallPos As AsmCommand Pointer, GotoPos As AsmCommand Pointer) As Integer
  'Calculates the size in words of an assembly code line
  'CallPos is the location of the call instruction in the instruction list

  Dim As Integer InstSize, PresPos, TempDataCount, CalledSub, CalledSubPage
  Dim As String CurrLineVal, TempData(20), NextCallTarget
  Dim As Integer RestorePage, NextCalledSub, NextCalledSubPage
  Dim As SubType Pointer GotoSub
  Dim As AsmCommand Pointer InstIndex

  Dim As String ROMData, Temp
  Dim As Integer DataBlockSize, RSC, DWIC, SS

  CurrLineVal = CurrLine

  'Replace PRESERVE with whatever is being preserved, need to do this for string tables
  IF Left(CurrLineVal, 9) = "PRESERVE " THEN
    PresPos = VAL(Mid(CurrLineVal, 10))
    CurrLineVal = ";" + PreserveCode(PresPos)
    If Mid(CurrLineVal, 2, 1) = Chr(8) Then CurrLineVal = Mid(CurrLineVal, 3)
  END If

  InstSize = 0
  InstIndex = IsASM(CurrLineVal)

  If InstIndex = 0 Then
    'If line not recognised as instruction, check for directive
    If ModePIC Then

      'Bank selection directives
      If Left(CurrLineVal, 9) = " banksel " Then
        If ChipFamily = 12 Then
          InstSize = 1
        ElseIf ChipFamily = 14 Then
          If HasSFRBit("RP1") Then
            InstSize = 2
          Else
            InstSize = 1
          End If
        ElseIf ChipFamily = 15 Then
          InstSize = 1
        ElseIf ChipFamily = 16 Then
          InstSize = 1
        End If

      ElseIf Left(CurrLineVal, 10) = " bankisel " Then
        InstSize = 1

      ElseIf Left(CurrLineVal, 7) = " fcall " Then
          'added for PIC-AS support
          InstSize = 3

      ElseIf Left(CurrLineVal, 6) = " ljmp " Then
          'added for PIC-AS support
           InstSize = 3

      ElseIf Left(CurrLineVal, 9) = " pagesel " Then
        If ChipFamily = 12 Or ChipFamily = 15 Then
          InstSize = 1
        Else
          InstSize = 2
        End If

      'Data embedding directives
      ElseIf Left(CurrLineVal, 4) = " dw " OR Left(CurrLineVal, 4) = " db " Or Left(CurrLineVal, 4) = " de " THEN
        'Print "Data: "; CurrLineVal;
        DataBlockSize = 1
        If (ChipFamily = 14 Or ChipFamily = 15) And Left(CurrLineVal, 4) = " de " Then DataBlockSize = 2
        ROMData = Trim(Mid(CurrLineVal, 4))
        InstSize = 0
        'DWC = 0
        RSC = 0

        DO WHILE INSTR(ROMData, Chr(34)) <> 0
          Temp = Mid(ROMData, INSTR(ROMData, Chr(34)) + 1)
          Temp = Chr(34) + Left(Temp, INSTR(Temp, Chr(34)))
          RSC = RSC + 1
          Replace ROMData, Temp, "%S" + Str(RSC) + "S"
          Temp = Mid(Temp, 2)
          Temp = Left(Temp, LEN(Temp) - 1)
          TempData(RSC) = Temp
        LOOP

        DWIC = 0
        DO WHILE INSTR(ROMData, ",") <> 0
          DWIC += 1: CheckTemp(DWIC) = Trim(Left(ROMData, INSTR(ROMData, ",") - 1))
          ROMData = Trim(Mid(ROMData, INSTR(ROMDATA, ",") + 1))
        LOOP
        IF ROMData <> "" Then
          DWIC += 1: CheckTemp(DWIC) = ROMData
        End If

        SS = 0
        InstSize = 0
        Do While SS < DWIC
          SS += 1

          Temp = CheckTemp(SS)
          IF Temp <> "" THEN
            'Temp contains integer
            IF IsConst(Temp) Then
              'Print "Int", MakeDec(Temp)
              If MakeDec(Temp) > 255 Then
                InstSize += 2
              Else
                InstSize += 1
              End If

            'Temp contains string
            Else
              IF INSTR(Temp, "%S") <> 0 THEN
                Temp = TempData(Val(Mid(Temp, INSTR(Temp, "%S") + 2)))
              END If
              InstSize += LEN(Temp)
            END If

          END IF
        Loop

        If ChipFamily = 16 Then
          InstSize = (InstSize + 1) \ 2
        End If
        'Print " Size:"; InstSize
      End If

    ElseIf ModeAVR Then
      'Data embedding directives
      If UCase(Left(Trim(CurrLineVal), 4)) = ".DB " THEN
        'Print "Data: "; CurrLineVal;
        ROMData = Trim(Mid(CurrLineVal, 4))
        InstSize = 0

        DO WHILE INSTR(ROMData, ",") <> 0
          InstSize += 1
          ROMData = Trim(Mid(ROMData, INSTR(ROMDATA, ",") + 1))
        LOOP
        IF ROMData <> "" Then
          InstSize += 1
        End If
        'Will be zero padded if odd length
        If InstSize Mod 2 = 1 Then InstSize += 1
        'Two bytes are packed in each location
        InstSize = InstSize \ 2

        'Print " Size:"; InstSize
      End If

    End If

  Else
    InstSize = InstIndex->Words
  End If

  Return InstSize
  End Function

Sub CalcSubSize(CurrSub As SubType Pointer)

  Dim As Integer FinalSize, ThisSubPage
  Dim As LinkedListElement Pointer CurrLine, NextLine
  Dim As String NextLineValue
  Dim As AsmCommand Pointer CallPos, GotoPos, RetPos

  'Get the position of call instruction, saves work later
  CallPos = IsASM("call")

  'Get the position of return instruction
  If ModePIC Then
    If ChipFamily = 12 Then
      RetPos = IsASM("retlw")
    Else
      RetPos = IsASM("return")
    End If
    GotoPos = IsASM("goto")
  ElseIf ModeAVR Or ModeZ8 Then
    RetPos = IsASM("ret")
    GotoPos = 0
  EndIf

  'Add page selection commands to Sub so that size is accurate
  '(Will remove and re-add all page selection commands)
  AddPageCommands(CurrSub)

  'Find the page this Sub is intended for
  ThisSubPage = CurrSub->DestPage

  FinalSize = 0
  CurrLine = CurrSub->CodeStart->Next
  Do While CurrLine <> 0

    'Get next code line
    NextLine = CurrLine->Next
    If NextLine <> 0 Then
      Do While (Left(NextLine->Value, 1) = ";" Or Left(NextLine->Value, 8) = "PRESERVE") AND NextLine->Next <> 0
        NextLine = NextLine->Next
      Loop
    End If
    NextLineValue = ""
    If NextLine <> 0 Then
      NextLineValue = NextLine->Value
    End If

    FinalSize += CalcLineSize(CurrLine->Value, ThisSubPage, CallPos, GotoPos)
    CurrLine = CurrLine->Next
  Loop

  'Add size of return
  If Not CurrSub->NoReturn Then
    FinalSize += RetPos->Words
  End If

  CurrSub->HexSize = FinalSize

  If FinalSize > CurrSub->MaxHexSize Then
    CurrSub->MaxHexSize = FinalSize
  End If
  
  End Sub

Function CastOrder (InType As String) As Integer
  Select Case UCase(Trim(InType))
    Case "BIT": Return 0
    Case "BYTE": Return 1
    Case "WORD": Return 2
    Case "INTEGER": Return 3
    Case "LONG": Return 4
    Case "ULONGINT": Return 5
    Case "LONGINT": Return 5
    Case "SINGLE": Return 6
    Case "DOUBLE": Return 7
    Case "STRING": Return 8
    Case Else: Return -1
  End Select
  END FUNCTION

  /'* \brief return a tag value
  \param ConstName As String, Origin As String
  \returns Nothing

  This Sub validates the constname against a set of rules.
  Issues Warning if the constname is not valid.

  '/
Sub CheckConstName (ConstName As String, Origin As String)

  Dim As String TempData
  Dim As Integer T

  'Names which will cause trouble
  T = 0
  IF INSTR(ConstName, " ") <> 0 THEN T = 1
  IF INSTR(ConstName, ",") <> 0 THEN T = 1
  IF INSTR(ConstName, "(") <> 0 THEN T = 1
  IF INSTR(ConstName, ")") <> 0 THEN T = 1
  IF ConstName = "W" THEN T = 1
  IF ConstName = "F" THEN T = 1
  IF ConstName = "B" THEN T = 1
  IF ConstName = "C" THEN T = 1
  IF ConstName = "Z" THEN T = 1
  IF T = 1 THEN
    TempData = Message("BadConstName")
    Replace TempData, "%const%", ConstName
    LogError TempData, Origin
  END IF

  'Names which may cause trouble
  If Len(ConstName) = 1 And T = 0 Then
    TempData = Message("WarningShortName")
    Replace TempData, "%type%", "constant"
    Replace TempData, "%name%", ConstName
    LogWarning TempData, Origin
  END If
  End Sub

  /'* \brief return a tag value
  \param None
  \returns Nothing

  Check speed that has been selected for the system clock, using information from DAT.
  The available clock speed are in the array IntOscSpeeds()
  If it is 0 Mhz, need to set to highest possible
  If the clock speed exceeds the maximum, i.e. overclocking, then show a warning


  '/
Sub CheckClockSpeed

  Dim As Integer CurrSpeed
  Dim As Double MaxSpeed
  Dim As String SpeedMessage

  If ChipMhz = 0 Then

    'Find highest int osc speed
    MaxSpeed = -1
    If IntOscSpeeds <> 0 Then
      For CurrSpeed = 1 To IntOscSpeeds
        If MaxSpeed < IntOscSpeed(CurrSpeed) Then
          MaxSpeed = IntOscSpeed(CurrSpeed)
        End If
      Next
    End If

    'No suitable int osc, use highest ext osc speed
    If MaxSpeed = -1 Then
      MaxSpeed = ChipMaxSpeed
    End If

    'Set speed
    ChipMHz = MaxSpeed
    SpeedMessage = Message("AutoClockSpeed")
    Replace SpeedMessage, "%speed%", Str(ChipMhz)
    IF VBS = 1 THEN PRINT SPC(5); SpeedMessage

  ElseIf ChipMhz > ChipMaxSpeed Then
    'Show warning if speed too high
    LogWarning(Message("WarningOverclocked"))
  End If
  
  End Sub

  /'* \brief return a tag value
  \param None

  This Sub compiles the source program.
  The preprocessor has aleady transformed the source files.

  Checks every Sub in program, compile those that need to be compiled.
  The approach includes the need to check again once a Sub has been compiled, because that Sub may require other subs.


  '/

Sub CompileProgram

  Dim As Integer CurrSub, CompileMore, IntLoc, CurrInc, SubLoc, TablesCompiled

  'Set constant here to ensure the preprocessor has examined the source
  If ModeAVR Then
    If HMult Then AddConstant("HARDWAREMULT", "")
  End If

  TablesCompiled = 0

  'Request initialisation routine
  If UserCodeOnlyEnabled = 0 then
      RequestSub(0, DefaultInitSys)
      SubLoc = LocationOfSub(DefaultInitSys, "")
      If SubLoc > 0 Then
        SourceFile(Subroutine(SubLoc)->SourceFile).InitSubUsed = -1
      End If
  End if
  'If main source file has startup routine, also call that
  With SourceFile(1)
    If .InitSub <> "" Then
      RequestSub(0, .InitSub)
      .InitSubUsed = -1
    End If
  End With

  'Find Interrupt sub, if found mark as required and set UserInt flag
  IntLoc = LocationOfSub("INTERRUPT", "")   'was capitalised
  UserInt = 0
  If IntLoc <> 0 Then
    Subroutine(IntLoc)->Required = -1
    UserInt = -1
  End If

  'Add code to prevent program continuing into subs
  AddMainEndCode

  'Need to find all DIM statements before anything else
  For CurrSub = 0 To SBC
    CompileDim Subroutine(CurrSub)
  Next

  'Main Loop
  If VBS <> 0 Then
    Print Spc(5); Message("CompSubs")
  End If
  Do
    CompileMore = 0
    For CurrSub = 0 To SBC
      With *Subroutine(CurrSub)
        If .Required And Not .Compiled Then
          CompileSubroutine(Subroutine(CurrSub))
          CompileMore = -1
        End If
      End With
    Next

    'Check for required initialisation subs
    FOR CurrInc = 1 TO SourceFiles
      With SourceFile(CurrInc)
        If .InitSub <> "" And .InitSubUsed Then
          SubLoc = LocationOfSub(.InitSub, "")
          If SubLoc <> 0 Then
            Subroutine(SubLoc)->FirstPage = -1
            If Not Subroutine(SubLoc)->Required Then
              Subroutine(SubLoc)->Required = -1
              CompileMore = -1
            End If
          End If
        End If
      End With
    Next

    'On last loop through, compile tables and then force one more loop
    If Not CompileMore And Not TablesCompiled Then
      'Compile data/string tables
      CompileTables
      TablesCompiled = -1

      CompileMore = -1
    End If

  Loop While CompileMore

  'Add context save/restore and int handlers to Interrupt
  AddInterruptCode

  'Compile IntOn/IntOff
  '(Need to compile here, after all On Interrupt commands in all subs have been found, but before AddMainInitCode)
  For CurrSub = 0 To SBC
    If .Subroutine(CurrSub)->Required Then
      CompileIntOnOff (Subroutine(CurrSub))
    End If
  Next

  'Ensure no overlap between temporary variables
  FixTemporaryVariables

  'Add initialise code to start of main
  AddMainInitCode

  'Add code for individual pin dir set on PIC 12 bit
  If ChipFamily = 12 Then
    FixSinglePinSet
  End If
  
  End Sub


  /'* \brief return a tag value
  \param CompSub as pointer

  This Sub compiles the subroutines.

  Supports use of ExtendedVerboseMessages= n | y in INI file to enable extended complilation messages

  '/
Sub CompileSubroutine(CompSub As SubType Pointer)

  If VBS = 1 Then
    Print ""
    Print Spc(5); "Commpiling method"; "  "; CompSub->Name
  End If

  'Split any lines at : (these may be inserted through constants)
  If ExtendedVerboseMessages Then Print Spc(15); "Splitting lines"
  SplitLines (CompSub)

  'Apply any tweaks
  TidyInputSource (CompSub)

  'Compile calls to other subroutines, insert macros
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling sub calls"

  CompileSubCalls (CompSub)

  'Compile DIMs again, in case any come through from macros
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling DIM"
  CompileDim (CompSub)

  'Compile various commands
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling For"
  CompileFor (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Processing arrays"
  ProcessArrays (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Adding sys var bits"
  AddSysVarBits (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling ReadTable"
  CompileReadTable (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling Pot"
  CompilePot (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling Do"
  CompileDo (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling Dir"
  CompileDir (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling Wait"
  CompileWait (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling On Interrupt"
  CompileOn (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling Set"
  CompileSet (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling Rotate"
  CompileRotate (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling Repeat"
  CompileRepeat (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling Select"
  CompileSelect (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling Return"   'was Select
  CompileReturn (CompSub)
  'Compile If statements and variable assignments last
  'This allows other commands to generate IFs and assignments rather than having to produce assembly
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling If"
  CompileIF (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling var assignments"
  CompileVars (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling Exit Sub"
  CompileExitSub (CompSub)
  If ExtendedVerboseMessages Then Print Spc(15); "Compiling Goto"
  CompileGoto (CompSub)

  'Recognise ASM
  If ExtendedVerboseMessages Then Print Spc(15); "Finding assembly"
  FindAssembly(CompSub)

  'Replace SysPointerX pseudo variable
  If ModeAVR Then FixPointerOps (CompSub)

  'CompileIntOnOff (CompSub) Need to do this after all subs compiled
  If ExtendedVerboseMessages Then Print Spc(15); "Done"
  CompSub->Compiled = -1
  CompiledSBC =  CompiledSBC + 1
  End Sub

  /'* \brief return a tag value
  \param SUM As String, AV As String, Origin As String, ByRef OutList As CodeSection Pointer = 0, NeverLast As Integer = 0

  This Sub compiles the CALC maths

  '/

  ' 'Check AV (the target variable) is not the source variable
  ' Dim sourcevariablepos as integer
  ' Dim lengthofsourcevariable as integer
  ' sourcevariablepos=  INSTR ( SUM, AV)
  ' lengthofsourcevariable = length ( AV )
  ' If sourcevariablepos+lengthofsourcevariable = length(SUM) then

  ' End if

Sub CompileCalc (SUM As String, AV As String, Origin As String, ByRef OutList As CodeSection Pointer = 0, NeverLast As Integer = 0)

  Dim As String TempData, InBrackets, OutTemp
  Dim As Integer T, BL, BC
  Dim As LinkedListElement Pointer FindLine

  If OutList = 0 Then
    OutList = NewCodeSection
  End If



  If ModeAVR Then

    'Turn off HMult when really complex as the register management just is too complex with SystempX variables being overwritten by the calc and MUL results
    'So, disable hardware maths and use long maths to ensure we have regiser management
    If CountOccur(Sum, "';+-*/%&|#!") > 3 And HMult Then
      'print Origin, Instr( UCASE( Origin), ";?F1L" )
      'Are we in the user program? Test for ;?F1L....  Because we dont change library code
      If Instr( UCASE( Origin), ";?F1L" ) > 0 Then
        ' LogWarning "Using AVR hardware maths - simplify statement to ensure correct ASM generation"
        If PreserveMode <> 0 Then OutList->CodeEnd = LinkedListInsert(OutList->CodeEnd, ";Complex math calculation.  AVR hardware MUL directive disabled  - simplify calculation to enable MUL for Byte math")
        HMult = 0
      End If
    End if

  End If


  'Simplify --, +-
  DO WHILE INSTR(SUM, "--") <> 0: Replace SUM, "--", "+": Loop
  DO WHILE INSTR(SUM, "+-") <> 0: Replace SUM, "+-", "-": LOOP

  'Calculate brackets
  Do While INSTR(SUM, "(") <> 0
    'Get the sum in brackets
    TempData = Mid(SUM, INSTR(SUM, "("))
    BL = 1
    FOR T = 2 TO LEN(TempData)
      IF Mid(TempData, T, 1) = "(" THEN BL = BL + 1
      IF Mid(TempData, T, 1) = ")" THEN BL = BL - 1
      IF BL = 0 THEN TempData = Left(TempData, T): EXIT FOR
    Next

    'Detect if whole sum is in brackets, remove brackets and try again if so
    If Len(TempData) = Len(SUM) Then
      SUM = Mid(Sum, 2, Len(Sum) - 2)
    Else

      'Use recursion to calculate sum in brackets
      InBrackets = Mid(TempData, 2)
      InBrackets = Left(InBrackets, LEN(InBrackets) - 1)
      OutTemp = ""
      CompileCalc InBrackets, OutTemp, Origin, OutList, -1
      If OutTemp = "" Then OutTemp = InBrackets
      Replace SUM, TempData, OutTemp
    End If
  Loop

  'Calculate unary operations
  CalcOps OutList, SUM, AV, "U!-", Origin, NeverLast

  'Calculate binary operations
  CalcOps OutList, SUM, AV, "*/%", Origin, NeverLast
  CalcOps OutList, SUM, AV, "+-", Origin, NeverLast
  CalcOps OutList, SUM, AV, "=~<>{}", Origin, NeverLast
  CalcOps OutList, SUM, AV, "&|!#", Origin, NeverLast

  'Re-add asm calculations
  FindLine = OutList->CodeList
  Do While FindLine <> 0
    TempData = UCase(FindLine->Value)
    IF INSTR(TempData, "SYSASMCALC") <> 0 Then
      T = VAL(Mid(TempData, INSTR(TempData, "SYSASMCALC") + 10))
      Replace FindLine->Value, "SYSASMCALC" + Str(T), CalcAtAsm(T)
    End If
    FindLine = FindLine->Next
  Loop
  
  End Sub

  /'* \brief return a tag value
  \param OutList As CodeSection Pointer, V1 As String, Act As String, V2 As String, Origin As String, Answer As String

  This Sub compiles the ADD and SUBTRACTION maths

  '/
Function CompileCalcAdd(OutList As CodeSection Pointer, V1 As String, Act As String, V2 As String, Origin As String, Answer As String) As String

  Dim OutVal As LongInt
  Dim As String V1Type, V2Type, CalcType, DestType, AV, R1, R2, Temp, V1Org, V2Org
  Dim As Integer SourceSub, DestSub
  Dim As String CurrV1, CurrV2
  Dim As String Cmd, Ovr, TempVar
  Dim As Integer CD, CurrVarByte, CheckCarry, AddEndIf, UseAVByte

  Dim As LinkedListElement Pointer CurrLine, NewCode
  CurrLine = OutList->CodeEnd

  V1Org = V1
  V2Org = V2
 
  'Calculate +, -. Replace sum with variable containing answer

  'Notes for new code:
  ' - Only need to work with enough bytes to fill answer. If A = 257, B = 256,
  '   A - B = 1 (with byte ops). If high bytes don't match, it will be
  ' impossible to store result anyway. No point calculating more bytes!
  ' - Remember references! (@variable)
  ' - addlw/sublw not implemented on 12 bit core
  ' - in long addition, need incfsz instruction on 16F to handle carry properly
  'Optimisations:
  ' - A +/- 0 = A
  ' - B +/- 0 = B
  ' - If A = B, A - B = 0, A + B = A << 1
  ' - A + 1/A - 1/B + 1/B - 1: use incf/decf/inc/dec?
  ' - 0 +/- 0: store 0 directly in output

  'Remove casts from variable names
  'These should have already been read by CalcOps, and a suitable size temp
  'or output variable should have been set.
  V1 = DelType(V1): V2 = DelType(V2)

  'Get types
  SourceSub = GetSubID(Origin)
  If INSTR(Origin, "D") <> 0 Then DestSub = GetDestSub(Origin) Else DestSub = SourceSub
  V1Type = TypeOfValue(V1, Subroutine(SourceSub))
  V2Type = TypeOfValue(V2, Subroutine(SourceSub))
  
  'override type for Floats - this is OK as TypeOfValue() would handle if... V1 or V2 had not been stripped of CAST
  If Instr( V1Org, "[SINGLE]" ) Then
    V1Type = "SINGLE"
  End If
  If Instr( V1Org, "[DOUBLE]" ) Then
    V1Type = "DOUBLE"
  End If
  If Instr( V2Org, "[SINGLE]" ) Then
    V2Type = "SINGLE"
  End If
  If Instr( V2Org, "[DOUBLE]" ) Then
    V2Type = "DOUBLE"
  End If
  DestType = TypeOfVar(Answer, Subroutine(DestSub))
  CalcType = DestType

  If ( V1Type = "BIT" AND ( Instr( V1, ".") <> 0 ))  Or  ( V2Type = "BIT"  AND ( Instr( V2, ".") <> 0  ))  Then
      LogError Message("SynIllegalBitMaths"), Origin
  End if

  'Remove cast from output var
  Answer = DelType(Answer)

  'Get output var
  AV = Answer
  
  If (( compilerdebug and cCOMPILECALCADD ) = cCOMPILECALCADD ) Then
    Print " 1 COMPILECALCADD: " + AV + " = " + V1 + " " + Act + " " + V2 + " TYPE: " + V1Type + ACT + V2Type + " " +  " CONST: " + Str(IsConst(V1)) + " " +Str(IsConst(V2))+ " CalcType: " + CalcType
  End If

  'Check if both are constants
  IF IsConst(V1) AND IsConst(V2) Then
    If InStr(V1, "@") = 0 AND INSTR(V2, "@") = 0 Then
      If IsIntType(CalcType) Then
        OutVal = 0
        IF Act = "+" THEN OutVal = MakeDec(V1) + MakeDec(V2)
        IF Act = "-" THEN OutVal = MakeDec(V1) - MakeDec(V2)
        If CalcType = "BYTE" Then
          Do While OutVal < 0: OutVal += 256: Loop
          Do While OutVal > 255: OutVal -= 256: Loop
        ElseIf CalcType = "WORD" Then
          Do While OutVal < 0: OutVal += 65536: Loop
          Do While OutVal > 65535: OutVal -= 65536: Loop
        ElseIf CalcType = "INTEGER" Then
          Do While OutVal < -32768: OutVal += 65536: Loop
          Do While OutVal > 32767: OutVal -= 65536: Loop
        ElseIf CalcType = "LONG" Then
          Do While OutVal < 0: OutVal += 2^32: Loop
          Do While OutVal >= 2^32: OutVal -= 2^32: Loop
        End If
        AV = Str(OutVal)
      ElseIf IsFloatType(CalcType) Then
        IF Act = "+" THEN AV = Str(MakeDecFloat(V1) + MakeDecFloat(V2))
        IF Act = "-" THEN AV = Str(MakeDecFloat(V1) - MakeDecFloat(V2))
      End If
      GOTO AddSubAnswer
    Else
      'CalcAtAsm(T), CAAC
      '@SYSASMCALCn
      'Need to put calculation for assembler into CalcAtAsm(n)
      'Then put "@SYSASMCALCn" into the the line. It will be replace when suitable.
      For CurrVarByte = 0 To GetTypeSize(CalcType) - 1
        CAAC += 1
        CalcAtAsm(CAAC) = GetByte("@" + V1 + Act + V2, CurrVarByte)
        NewCode = CompileVarSet("@SYSASMCALC" + Str(CAAC), "[byte]" + GetByte(AV, CurrVarByte), Origin)
        CurrLine = LinkedListInsertList(CurrLine, NewCode)
      Next
      GoTo AddSubAnswer
    End If
  END IF

  'Optimisations
  'Increment/Decrement (byte only)
  'V1 and V2 are the two values in the calculation step, and R1 and R2 are the registers that they have been temporarily copied into on AVR.
  If CalcType = "BYTE" Then
    If V2 = "1" And INSTR(V1, "@") = 0 Then
      If ModePIC Then
        If V1 <> AV Then
          If Act = "+" Then CurrLine = LinkedListInsert(CurrLine, " incf " + V1 + ",W")
          If Act = "-" Then CurrLine = LinkedListInsert(CurrLine, " decf " + V1 + ",W")
          CurrLine = LinkedListInsert(CurrLine, " movwf " + AV)
        Else
          If Act = "+" Then CurrLine = LinkedListInsert(CurrLine, " incf " + V1 + ",F")
          If Act = "-" Then CurrLine = LinkedListInsert(CurrLine, " decf " + V1 + ",F")
        End If
      ElseIf ModeAVR Then
        NewCode = LinkedListCreate
        R1 = PutInRegister(NewCode, V1, "BYTE", Origin)
        CurrLine = LinkedListInsertList(CurrLine, NewCode)
        If Act = "+" Then CurrLine = LinkedListInsert(CurrLine, " inc " + R1)
        If Act = "-" Then CurrLine = LinkedListInsert(CurrLine, " dec " + R1)
        AV = R1
      End If
      Goto AddSubAnswer
    End If
    If V1 = "1" And Act = "+" And INSTR(V2, "@") = 0 Then
      If ModePIC Then
        If V2 <> AV Then
          CurrLine = LinkedListInsert(CurrLine, " incf " + V2 + ",W")
          CurrLine = LinkedListInsert(CurrLine, " movwf " + AV)
        Else
          CurrLine = LinkedListInsert(CurrLine, " incf " + V2 + ",F")
        End If
      ElseIf ModeAVR Then
        NewCode = LinkedListCreate
        R2 = PutInRegister(NewCode, V2, "BYTE", Origin)
        CurrLine = LinkedListInsertList(CurrLine, NewCode)
        CurrLine = LinkedListInsert(CurrLine, " inc " + R2)
        AV = R2
      End If
      Goto AddSubAnswer
    End If
  End If

  If V1 = V2 And INSTR(V1, "@") = 0 Then
    If Act = "-" Then
      AV = "0"
      Goto AddSubAnswer
    End If
  End If

  'Handle floating point addition here
  If IsFloatType(CalcType) Then

    'Check for zeroes that mean addition or subtraction can be swapped
    If IsConst(V2) Then
      If MakeDecFloat(V2) = 0 Then
        AV = V1
        GoTo AddSubAnswer
      End If
    ElseIf IsConst(V1) And Act = "+" Then
      If MakeDecFloat(V1) = 0 Then
        AV = V2
        GoTo AddSubAnswer
      End If
    End If

    'Copy V1 and V2 into temporary variables, call appropriate sub
    AddVar("Sys" + CalcType + "TempA", CalcType, 1, 0, "REAL", Origin, ,-1)
    AddVar("Sys" + CalcType + "TempB", CalcType, 1, 0, "REAL", Origin, ,-1)
    AddVar("Sys" + CalcType + "TempX", CalcType, 1, 0, "REAL", Origin, ,-1)
    CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V1, "Sys" + CalcType + "TempA", Origin))
    CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V2, "Sys" + CalcType + "TempB", Origin))
    If Act = "+" Then
      RequestSub(Subroutine(SourceSub), "SysAddSub" + CalcType, "")
      CurrLine = LinkedListInsert(CurrLine, " call SysAddSub" + CalcType)
    ElseIf Act = "-" Then
      RequestSub(Subroutine(SourceSub), "SysSubSub" + CalcType, "")
      CurrLine = LinkedListInsert(CurrLine, " call SysSubSub" + CalcType)
    End If
    AV = "Sys" + CalcType + "TempX"
    GoTo AddSubAnswer
  End If

  'Swap V1, V2 if mode is add and V1 is const
  If Act = "+" And IsConst(V1) Then
    'Swap names
    Swap V1, V2

    'Swap Types
    Swap V1Type, V2Type
  End If

  'Snippet of old code, can sublw be used below to improve generated code?
  '   If ChipFamily <> 12 And IsConst(V1) Then
  '     Cmd = " addlw ": IF Act = "-" THEN Cmd = " sublw "
  '     CurrLine = LinkedListInsert(CurrLine, Cmd + GetByte(V1, 0))
  '     CurrLine = LinkedListInsert(CurrLine, " movwf " + AV)
  '

  'Flag to indicate if carry/borrow check is needed
  CheckCarry = 0

  'Deal with each byte
  For CurrVarByte = 0 To GetTypeSize(CalcType) - 1

    AddEndIf = 0

    'Get current byte of V1 and V2 (if they exist) or 0
    If GetTypeSize(V1Type) > CurrVarByte Then
      CurrV1 = GetByte(V1, CurrVarByte)
    Else
      CurrV1 = "0"
    End If
    If GetTypeSize(V2Type) > CurrVarByte Then
      CurrV2 = GetByte(V2, CurrVarByte)
    Else
      CurrV2 = "0"
    End If

    'Shortcut for multiple byte increment
    '(Especially useful for pointers and For loops)
    If V1 = AV And V2 = "1" And INSTR(V1, "@") = 0 And Act = "+" Then
      If ModePIC Then
        If CurrVarByte > 0 Then
          CurrLine = LinkedListInsert(CurrLine, " btfsc STATUS,Z")
        End If
        CurrLine = LinkedListInsert(CurrLine, " incf " + CurrV1 + ",F")

      ElseIf ModeAVR Then
        NewCode = LinkedListCreate
        R1 = PutInRegister(NewCode, CurrV1, "BYTE", Origin)
        CurrLine = LinkedListInsertList(CurrLine, NewCode)
        If CurrVarByte > 0 Then
          CurrLine = LinkedListInsert(CurrLine, " brne PC + 2")
        End If
        CurrLine = LinkedListInsert(CurrLine, " inc " + R1)
        'Copy result to output variable
        NewCode = CompileVarSet("[byte]" + R1, "[byte]" + GetByte(AV, CurrVarByte), Origin)
        CurrLine = LinkedListInsertList(CurrLine, NewCode)
        FreeCalcVar R1
      End If
      Goto AddSubNextByte

    End If

    'Is a calculation needed?
    'If both inputs 0, output 0
    'If one input 0, copy or negate (0 - x) other input
    'If both inputs non 0, then calculate. Check carry if needed (CurrVarByte > 0 and potential overflow)

    'Both inputs 0, output 0
    If CurrV1 = "0" And CurrV2 = "0" Then
      'Need carry?
      If CheckCarry Then
        'Carry won't be needed next time
        CheckCarry = 0

        If ModePIC Then
          '0 + C > F, no need to update C
          CurrLine = LinkedListInsert(CurrLine, " clrf " + GetByte(AV, CurrVarByte))
          If Act = "+" Then
            CurrLine = LinkedListInsert(CurrLine, " btfsc STATUS,C")
            CurrLine = LinkedListInsert(CurrLine, " incf " + GetByte(AV, CurrVarByte) + ",F")
          Else
            CurrLine = LinkedListInsert(CurrLine, " btfss STATUS,C")
            'CurrLine = LinkedListInsert(CurrLine, " addlw 255")
            CurrLine = LinkedListInsert(CurrLine, " decf " + GetByte(AV, CurrVarByte) + ",F")
          End If
          'CurrLine = LinkedListInsert(CurrLine, " movwf " + GetByte(AV, CurrVarByte))
          GoTo AddSubNextByte

        ElseIf ModeAVR Then
          R1 = GetCalcVar("BYTE")
          CurrLine = LinkedListInsert(CurrLine, " clr " + R1)
          CurrLine = LinkedListInsert(CurrLine, " brcc PC + 2")
          If Act = "+" Then
            CurrLine = LinkedListInsert(CurrLine, " inc " + R1)
          Else
            CurrLine = LinkedListInsert(CurrLine, " dec " + R1)
          End If
          NewCode = CompileVarSet(R1, "[BYTE]" + GetByte(AV, CurrVarByte), Origin)
          CurrLine = LinkedListInsertList(CurrLine, NewCode)
          FreeCalcVar R1
          GoTo AddSubNextByte

        End If

      Else
        'No high bytes, no carry, so put 0 into output byte
        NewCode = CompileVarSet("0", "[BYTE]" + GetByte(AV, CurrVarByte), Origin)
        CurrLine = LinkedListInsertList(CurrLine, NewCode)
        GoTo AddSubNextByte
      End If

    End If

    'Need to calculate AV
    If ModePIC Then
      'On 16F1/18F, if V1 is const, put it into variable so subwfb can be used
      'On 12 bit core, V1 cannot be a constant due to lack of sublw instruction
      'If V1 is a constant, it needs to be put into a variable
      If IsConst(CurrV1) And (ChipFamily = 12 Or ((ChipFamily = 15 Or ChipFamily = 16) And CheckCarry)) Then
        CurrLine = LinkedListInsert(CurrLine, " movlw " + CurrV1)
        CurrV1 = GetCalcVar("BYTE")
        CurrLine = LinkedListInsert(CurrLine, " movwf " + CurrV1)
      END If

      'Need to put V1 into temporary variable if carry involved, or issue occurs adding 255 to 255 with carry
      UseAVByte = 0
      If Not IsConst(CurrV1) And V1 <> AV And CheckCarry And ChipFamily <> 15 And ChipFamily <> 16 Then
        CurrLine = LinkedListInsert(CurrLine, " movf " + CurrV1 + ",W")
        CurrV1 = GetByte(AV, CurrVarByte)
        CurrLine = LinkedListInsert(CurrLine, " movwf " + CurrV1)
        UseAVByte = -1
      End If

      'Put V2 into W
      Temp = " movf " + CurrV2 + ",W"
      IF IsConst(CurrV2) THEN Temp = " movlw " + CurrV2
      CurrLine = LinkedListInsert(CurrLine, Temp)

      'Add carry from low byte calc to W
      If CheckCarry And ChipFamily <> 15 And ChipFamily <> 16 Then
        Temp = " btfsc ": IF Act = "-" THEN Temp = " btfss "
        If ChipFamily = 12 Then
          TempVar = GetCalcVar("BYTE")
          CurrLine = LinkedListInsert(CurrLine, " movwf " + TempVar)
          CurrLine = LinkedListInsert(CurrLine, Temp + "STATUS,C")
          CurrLine = LinkedListInsert(CurrLine, " incf " + TempVar + ",W")
          FreeCalcVar TempVar
        Else
          CurrLine = LinkedListInsert(CurrLine, Temp + "STATUS,C")
          'CurrLine = LinkedListInsert(CurrLine, " addlw 1")
          If IsConst(CurrV2) Then
            CurrLine = LinkedListInsert(CurrLine, " movlw " + CurrV2 + " + 1")
          Else
            CurrLine = LinkedListInsert(CurrLine, " incf " + CurrV2 + ",W")
            ILC += 1: AddEndIf = -1
            CurrLine = LinkedListInsert(CurrLine, " btfsc STATUS,Z")
            CurrLine = LinkedListInsert(CurrLine, " goto ENDIF" + Str(ILC))
          End If
        End If
      End If

      'Add W to V1, store - C already added, or 18F and no C
      If (ChipFamily <> 15 And ChipFamily <> 16) Or Not CheckCarry Then
        If (V1 <> AV Or IsConst(CurrV1)) And Not UseAVByte Then
          If IsConst(CurrV1) Then
            If Act = "+" And CurrV1 <> "0" Then CurrLine = LinkedListInsert(CurrLine, " addlw " + CurrV1)
            If Act = "-" Then CurrLine = LinkedListInsert(CurrLine, " sublw " + CurrV1)
          Else
            If Act = "+" Then CurrLine = LinkedListInsert(CurrLine, " addwf " + CurrV1 + ",W")
            If Act = "-" Then CurrLine = LinkedListInsert(CurrLine, " subwf " + CurrV1 + ",W")
          End If
          If AddEndIf Then
            AddEndIf = 0
            CurrLine = LinkedListInsert(CurrLine, "ENDIF" + Str(ILC))
            GetMetaData(Currline)->IsLabel = -1
          EndIf
          CurrLine = LinkedListInsert(CurrLine, " movwf " + GetByte(AV, CurrVarByte))
        Else
          IF Act = "+" THEN CurrLine = LinkedListInsert(CurrLine, " addwf " + CurrV1 + ",F")
          IF Act = "-" THEN CurrLine = LinkedListInsert(CurrLine, " subwf " + CurrV1 + ",F")
          If AddEndIf Then
            AddEndIf = 0
            CurrLine = LinkedListInsert(CurrLine, "ENDIF" + Str(ILC))
            GetMetaData(Currline)->IsLabel = -1
          EndIf
        End If

      'Add W to V1, store - 18F, carry needed
      ElseIf ChipFamily = 15 Or ChipFamily = 16 Then
        If AV <> V1 Then
          IF Act = "+" THEN CurrLine = LinkedListInsert(CurrLine, " addwfc " + CurrV1 + ",W")
          IF Act = "-" THEN CurrLine = LinkedListInsert(CurrLine, " subwfb " + CurrV1 + ",W")
          CurrLine = LinkedListInsert(CurrLine, " movwf " + GetByte(AV, CurrVarByte))
        Else
          IF Act = "+" THEN CurrLine = LinkedListInsert(CurrLine, " addwfc " + CurrV1 + ",F")
          IF Act = "-" THEN CurrLine = LinkedListInsert(CurrLine, " subwfb " + CurrV1 + ",F")
        End If
      End If
      'Carry or borrow may now have occured, must check on next byte
      CheckCarry = -1

    ElseIf ModeAVR Then

      'Put parameters into registers
      'Overwrite V1?
      Ovr = "O:"
      If UCase(Left(V1, 7)) = "SYSTEMP" Then Ovr = ""
      If UCase(Left(V1, 11)) = "SYSCALCTEMP" Then Ovr = ""

      'Put V1 into register, keep name in R1
      NewCode = LinkedListCreate
      R1 = PutInRegister(NewCode, Ovr + CurrV1, "BYTE", Origin)
      CurrLine = LinkedListInsertList(CurrLine, NewCode)
      'Put V2 into register, keep name in R2
      NewCode = LinkedListCreate
      R2 = PutInRegister(NewCode, CurrV2, "BYTE", Origin)
      CurrLine = LinkedListInsertList(CurrLine, NewCode)
      If CurrVarByte = 0 Then
        AddVar V1, "BYTE", 1, Subroutine(SourceSub), "REAL", Origin
        AddVar V2, "BYTE", 1, Subroutine(SourceSub), "REAL", Origin
      End If

      If CheckCarry Then
        If Act = "+" Then CurrLine = LinkedListInsert(CurrLine, " adc " + R1 + "," + R2)
        If Act = "-" Then CurrLine = LinkedListInsert(CurrLine, " sbc " + R1 + "," + R2)
      Else
        If Act = "+" Then CurrLine = LinkedListInsert(CurrLine, " add " + R1 + "," + R2)
        If Act = "-" Then CurrLine = LinkedListInsert(CurrLine, " sub " + R1 + "," + R2)
      End If

      'Copy result to output variable
      NewCode = CompileVarSet("[byte]" + R1, "[byte]" + GetByte(AV, CurrVarByte), Origin)
      CurrLine = LinkedListInsertList(CurrLine, NewCode)
      FreeCalcVar R2
      FreeCalcVar R1
      'Carry or borrow may now have occured, must check on next byte
      CheckCarry = -1
    End If

    AddSubNextByte:

  Next

  'Replace sum with answer variable
  AddSubAnswer:
  OutList->CodeEnd = CurrLine
  Return AV
  
  End Function

Function CompileCalcCondition(OutList As CodeSection Pointer, V1 As String, Act As String, V2 As String, Origin As String, Answer As String) As String

  Dim As String V1O, V2O, V1Type, V2Type, CalcType, DestType, AV, R1, R2, AVH, CT1, CT2, SNT
  Dim As Integer SourceSub, DestSub
  Dim As String Cmd, CalcVarType, VarBit
  Dim As Integer OutVal, PD, IsBitTest
  Dim As LinkedListElement Pointer CurrLine
  CurrLine = OutList->CodeEnd

  'Calculate <,>,=,~. Replace sum with variable containing answer

  'Unusual symbols:
  ' ~ (not equal)
  ' } (equal or greater)
  ' { (less or equal)

  'Check if both are constants
  IF IsConst(V1) AND IsConst(V2) And INSTR(V1, "@") = 0 AND INSTR(V2, "@") = 0 THEN
    OutVal = 0
    IF Act = "=" THEN OutVal = MakeDec(V1) = MakeDec(V2)
    IF Act = "~" THEN OutVal = MakeDec(V1) <> MakeDec(V2)
    IF Act = "<" THEN OutVal = MakeDec(V1) < MakeDec(V2)
    IF Act = ">" THEN OutVal = MakeDec(V1) > MakeDec(V2)
    IF Act = "{" THEN OutVal = MakeDec(V1) <= MakeDec(V2)
    IF Act = "}" THEN OutVal = MakeDec(V1) >= MakeDec(V2)
    If OutVal Then OutVal = 255 Else OutVal = 0
    Return Str(OutVal)
  END IF

  'Get types
  SourceSub = GetSubID(Origin)
  If INSTR(Origin, "D") <> 0 Then DestSub = GetDestSub(Origin) Else DestSub = SourceSub
  V1Type = TypeOfValue(V1, Subroutine(SourceSub))
  V2Type = TypeOfValue(V2, Subroutine(SourceSub))
  If CastOrder(V1Type) > CastOrder(V2Type) Then
    CalcType = V1Type
  Else
    CalcType = V2Type
  End If
  DestType = "BYTE" 'Remove any doubt that may exist! (unlikely)
  V1 = DelType(V1): V2 = DelType(V2)
  V1O = V1: V2O = V2

  IsBitTest = 0
  If (TypeOfValue(V1, Subroutine(SourceSub)) = "BIT" Or IsConst(V1)) And (TypeOfValue(V2, Subroutine(SourceSub)) = "BIT" Or IsConst(V2)) Then
    IsBitTest = -1
  End If

  'Remove cast from output var
  Answer = DelType(Answer)

  'Generate asm code for sum

  'Special shortcut code for bit test
  If IsBitTest And IsConst(V2) Then

    'Translate less/more into equal/not equal
    If Act = "<" Or Act = ">" Then Act = "~"
    If Act = "{" Or Act = "}" Then Act = "="
    'Decide test status
    Dim As Integer TestFor = 0
    If Act = "=" And MakeDec(V2) <> 0 Then TestFor = 1
    If Act = "~" And MakeDec(V2) = 0 Then TestFor = 1

    'Get Bit name and number
    Dim As String BitName
    BitName = V1
    Replace BitName, ".", ","

    AddVar "SysByteTempX", "Byte", 1, Subroutine(SourceSub), "REAL", Origin, , -1
    If ModePIC Then
      'Bit referenced by variable?
      VarBit = Mid(V1, InStr(V1, ".") + 1)
      If Not IsConst(VarBit) And Not HasSFRBit(VarBit) Then
        CurrLine = LinkedListInsertList(CurrLine, CompileConditions(V1 + "=" + Str(TestFor), "FALSE", Origin, Subroutine(SourceSub)))
        CurrLine = LinkedListInsert(CurrLine, " btfss STATUS,Z")
      Else
        CurrLine = LinkedListInsert(CurrLine, " clrf SysByteTempX")
        If TestFor = 1 Then
          CurrLine = LinkedListInsert(CurrLine, " btfsc " + BitName)
        Else
          CurrLine = LinkedListInsert(CurrLine, " btfss " + BitName)
        End If
      End If

      CurrLine = LinkedListInsert(CurrLine, " comf SysByteTempX,F")
      GoTo CompileConditionDone
    ElseIf ModeAVR Then
    
      'Incorrect handler - this just set the CLR!! - corrected #1377
      CurrLine = LinkedListInsert(CurrLine, " ldi	SysValueCopy,255")
      CurrLine = LinkedListInsert(CurrLine, " mov	SYSBYTETEMPX,SysValueCopy")        
      'Pre 1377 change
      '        CurrLine = LinkedListInsert(CurrLine, " clr SysByteTempX")
      'End if #1377 change

      CurrLine = LinkedListInsertList(CurrLine, CompileConditions(V1 + "=" + Str(TestFor), "FALSE", Origin, Subroutine(SourceSub)))
      'Incorrect syntax, extra ,F... a tie over from PIC stuff... maybe Hugh made an error - corrected #1377 was " com SysByteTempX,F")
      CurrLine = LinkedListInsert(CurrLine, " com SysByteTempX")
      GoTo CompileConditionDone
    End If
  End If

  'Copy parameters
  If CalcType = "STRING" Then
    'Copy pointers
    'SetStringPointers (V1, V2, Origin)
    'Print "String comparison ", V1, V1Type, V2, V2Type, Origin
    CurrLine = LinkedListInsertList(CurrLine, SetStringPointers(V1, V2, Subroutine(SourceSub), Origin))
  Else
    CalcVarType = CalcType
    If CalcVarType = "BIT" Then CalcVarType = "BYTE"
    'declare SysCalcTempA, SysCalcTempB and SysCalcTempX
    AddVar "Sys" + CalcVarType + "TempA", CalcVarType, 1, Subroutine(SourceSub), "REAL", Origin, , -1
    AddVar "Sys" + CalcVarType + "TempB", CalcVarType, 1, Subroutine(SourceSub), "REAL", Origin, , -1
    AddVar "SysByteTempX", "BYTE", 1, Subroutine(SourceSub), "REAL", Origin, , -1

    'Copy values
  '   If Act = ">" Or Act = "}" Then
    If Act = ">" Or Act = "{" Then
      CT1 = "Sys" + CalcVarType + "TempB"
      CT2 = "Sys" + CalcVarType + "TempA"
    Else
      CT1 = "Sys" + CalcVarType + "TempA"
      CT2 = "Sys" + CalcVarType + "TempB"
    End If
    CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V1, CT1, Origin))
    CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V2, CT2, Origin))
  End If

  'Decide which Sub to use
  IF Act = "=" Or Act = "~" THEN SNT = "SysCompEqual"
  'IF Act = "<" Or Act = ">" THEN SNT = "SysCompLessThan"
  'IF Act = "{" OR Act = "}" THEN SNT = "SysCompLessOrEqual"
  IF Act = "<" Or Act = ">" Or Act = "{" OR Act = "}" Then SNT = "SysCompLessThan"
  IF CalcType = "WORD" THEN SNT += "16"
  IF CalcType = "INTEGER" THEN
    If Act = "=" Or Act = "~" Then
      SNT += "16"
    Else
      SNT += "INT"
    End If
  End If
  If CalcType = "LONG" Then SNT += "32"
  IF CalcType = "SINGLE" THEN SNT += "Single"
  IF CalcType = "STRING" THEN SNT += "String"

  'Call calculation sub
  SNT = UCase( SNT )     'added as SNT was case sensitive
  CurrLine = LinkedListInsert(CurrLine, " call " + SNT)
  RequestSub (Subroutine(SourceSub), SNT)

  'Invert answer for not equal
  'If Act = "~" Then
  If Act = "~" Or Act = "{" Or Act = "}" Then
    If ModePIC Then
      CurrLine = LinkedListInsert(CurrLine, " comf SysByteTempX,F")
    ElseIf ModeAVR Then
      CurrLine = LinkedListInsert(CurrLine, " com SysByteTempX")
    End If
  End If

  'Write answer
  CompileConditionDone:
  AV = "SysByteTempX"
  OutList->CodeEnd = CurrLine
  Return AV
  
  End Function

Function CompileCalcLogic (OutList As CodeSection Pointer, V1 As String, Act As String, V2 As String, Origin As String, Answer As String) As String

  Dim As String V1O, V2O, V1Type, V2Type, CalcType, DestType, AV, R1, R2
  Dim As String CurrV1, CurrV2, CurrAct, Temp, Cmd, Ovr
  Dim As Integer CD, SourceSub, DestSub, CurrVarByte
  Dim As LinkedListElement Pointer CurrLine, NewCode
  CurrLine = OutList->CodeEnd

  'Delete casts (not needed for logic operations)
  V1 = DelType(V1): V2 = DelType(V2)

  'Get types
  'Print V1, Act, V2, AV
  SourceSub = GetSubID(Origin)
  If INSTR(Origin, "D") <> 0 Then DestSub = GetDestSub(Origin) Else DestSub = SourceSub
  V1Type = TypeOfValue(V1, Subroutine(SourceSub))
  V2Type = TypeOfValue(V2, Subroutine(SourceSub))
  DestType = TypeOfVar(Answer, Subroutine(DestSub))
  CalcType = DestType

  'Remove cast from output var
  Answer = DelType(Answer)

  'Check types
  If V1Type = "SINGLE" Or V1Type = "STRING" Then
    Temp = Message("OperandTypeMismatch")
    Replace Temp, "%operand%", Act
    Replace Temp, "%type%", V1Type
    LogError Temp, Origin
  End If
  If V2Type = "SINGLE" Or V2Type = "STRING" Then
    Temp = Message("OperandTypeMismatch")
    Replace Temp, "%operand%", Act
    Replace Temp, "%type%", V2Type
    LogError Temp, Origin
  End If

  'Convert bit vars to byte
  If V1Type = "BIT" And Not IsConst(V1) Then
    V1Type = "BYTE"
    V1O = V1
    V1 = GetCalcVar("BYTE")
    CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V1O, V1, Origin))
  End If
  If V2Type = "BIT" And Not IsConst(V2) Then
    V2Type = "BYTE"
    V2O = V2
    V2 = GetCalcVar("BYTE")
    CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V2O, V2, Origin))
  End If

  'Get output var
  AV = Answer

  'If V1 and V2 are constant, produce constant result
  If (Act <> "!" And IsConst(V1) And IsConst(V2)) OR (Act = "!" And IsConst(V2)) Then
    IF Act = "&" THEN Return Trim(Str(MakeDec(V1) AND MakeDec(V2)))
    IF Act = "|" THEN Return Trim(Str(MakeDec(V1) OR MakeDec(V2)))
    IF Act = "#" THEN Return Trim(Str(MakeDec(V1) XOR MakeDec(V2)))
  End If

  'If V1 not const, V2 const, swap
  If IsConst(V2) Then
    Swap V1, V2
    Swap V1Type, V2Type
  End If

  'Perform operation on each pair of bytes
  For CurrVarByte = 0 To GetTypeSize(CalcType) - 1

    'Get current byte of V1 and V2 (if they exist) or 0
    If GetTypeSize(V1Type) > CurrVarByte Then
      CurrV1 = GetByte(V1, CurrVarByte)
    Else
      CurrV1 = "0"
    End If
    If GetTypeSize(V2Type) > CurrVarByte Then
      CurrV2 = GetByte(V2, CurrVarByte)
    Else
      CurrV2 = "0"
    End If
    CurrAct = Act

    'Optimisations for when one var is 255
    'Print "Before:", CurrV1, CurrAct, CurrV2
    If CurrV1 = "255" Then
      '255 & x = 0 | x
      If CurrAct = "&" Then
        CurrV1 = "0"
        CurrAct = "|"
      '255 | x = 0 | 255
      ElseIf CurrAct = "|" Then
        CurrV1 = "0"
        CurrV2 = "255"
      End If
    ElseIf CurrV2 = "255" Then
      'x & 255 = x | 0
      If CurrAct = "&" Then
        CurrV2 = "0"
        CurrAct = "|"
      'x | 255 = 0 | 255 (need V1 = 0 for below)
      ElseIf CurrAct = "|" Then
        CurrV1 = "0"
      End If
    End If

    'Should always try to have constant in V1 only
    If CurrV2 = "0" And Not IsConst(CurrV1) Then
      'Can swap order of each byte with no consequences
      'Don't need to swap types (VxType) either.
      Swap CurrV1, CurrV2
    End If
    'Print "After:", CurrV1, CurrAct, CurrV2

    'Possible inputs:
    'V1 = 0, V2 = 0 > AV = 0
    'V1 = 0, V2 = x > AV = V2 (OR/XOR) or 0 (AND)
    'V1 = x, V2 = x > AV = V1 op V2

    'First case, no V1 or V2
    If CurrV1 = "0" And CurrV2 = "0" Then
      '0 | 0 = 0, 0 & 0 = 0, 0 # 0 = 0
      If ModePIC Then
        CurrLine = LinkedListInsert(CurrLine, " clrf " + GetByte(AV, CurrVarByte))
      ElseIf ModeAVR Then
        NewCode = CompileVarSet("0", "[byte]" + GetByte(AV, CurrVarByte), Origin)
        CurrLine = LinkedListInsertList(CurrLine, NewCode)
      End If

    'Second case, V2 only
    ElseIf CurrV1 = "0" And CurrV2 <> "0" Then
      'Anything AND 0 = 0, so clear result
      If CurrAct = "&" Then
        If ModePIC Then
          CurrLine = LinkedListInsert(CurrLine, " clrf " + GetByte(AV, CurrVarByte))
        ElseIf ModeAVR Then
          'CurrLine = LinkedListInsert(CurrLine, " clr " + GetByte(AV, CurrVarByte))
          NewCode = CompileVarSet("0", "[byte]" + GetByte(AV, CurrVarByte), Origin)
          CurrLine = LinkedListInsertList(CurrLine, NewCode)
        End If

      'Anything OR/XOR 0 = Anything, so copy V2
      ElseIf CurrAct = "|" Or CurrAct = "#" Then
        NewCode = CompileVarSet("[byte]" + CurrV2, "[byte]" + GetByte(AV, CurrVarByte), Origin)
        CurrLine = LinkedListInsertList(CurrLine, NewCode)
      End If

    'Third case, V1 and V2
    ElseIf CurrV1 <> "0" And CurrV2 <> "0" Then
      If ModePIC Then
        Cmd = " movf " + GetByte(V1, CurrVarByte) + ",W"
        If IsConst(V1) Then Cmd = " movlw " + GetByte(V1, CurrVarByte)
        CurrLine = LinkedListInsert(CurrLine, Cmd)
        IF CurrAct = "&" THEN Cmd = " andwf "
        IF CurrAct = "|" THEN Cmd = " iorwf "
        IF CurrAct = "#" THEN Cmd = " xorwf "
        If CurrVarByte = 0 Then
          AddVar V2, "BYTE", 1, Subroutine(SourceSub), "REAL", Origin
        End If
        If V2 <> AV Then
          CurrLine = LinkedListInsert(CurrLine, Cmd + GetByte(V2, CurrVarByte) + ",W")
          CurrLine = LinkedListInsert(CurrLine, " movwf " + GetByte(AV, CurrVarByte))
        Else
          CurrLine = LinkedListInsert(CurrLine, Cmd + GetByte(V2, CurrVarByte) + ",F")
        End If

      ElseIf ModeAVR Then
        'If AVR, put values in registers
        NewCode = LinkedListCreate
        R1 = PutInRegister(NewCode, CurrV1, "BYTE", Origin)
        If CurrVarByte = 0 Then AddVar V1, "BYTE", 1, Subroutine(SourceSub), "REAL", Origin
        CurrLine = LinkedListInsertList(CurrLine, NewCode)
        'Overwrite V2?
        Ovr = "O:"
        If UCase(Left(V2, 7)) = "SYSTEMP" Then Ovr = ""
        If UCase(Left(V2, 11)) = "SYSCALCTEMP" Then Ovr = ""

        NewCode = LinkedListCreate
        R2 = PutInRegister(NewCode, Ovr + CurrV2, "BYTE", Origin)
        If CurrVarByte = 0 Then AddVar V2, "BYTE", 1, Subroutine(SourceSub), "REAL", Origin
        CurrLine = LinkedListInsertList(CurrLine, NewCode)

        IF CurrAct = "&" THEN CurrLine = LinkedListInsert(CurrLine, " and " + R2 + "," + R1)
        IF CurrAct = "|" THEN CurrLine = LinkedListInsert(CurrLine, " or " + R2 + "," + R1)
        IF CurrAct = "#" THEN CurrLine = LinkedListInsert(CurrLine, " eor " + R2 + "," + R1)

        'Copy result to output variable
        NewCode = CompileVarSet(R2, "[byte]" + GetByte(AV, CurrVarByte), Origin)
        CurrLine = LinkedListInsertList(CurrLine, NewCode)
        If R1 <> AV Then FreeCalcVar R2
        If R2 <> AV Then FreeCalcVar R1
      End If

    End If

  Next

  OutList->CodeEnd = CurrLine
  Return AV
  
  End Function

Function CompileCalcMult (OutList As CodeSection Pointer, V1 As String, Act As String, V2 As String, Origin As String, Answer As String) As String

  Dim As String V1O, V2O, V1Type, V2Type, CalcType, DestType, AV, R1, R2, AVH, SNT
  Dim As String Temp
  Dim As Integer SourceSub, DestSub, CD, PD
  Dim As LinkedListElement Pointer CurrLine, NewCode
  CurrLine = OutList->CodeEnd

  'Calculate *, /, %. Replace sum with variable containing answer

  'Get types
  SourceSub = GetSubID(Origin)
  If INSTR(Origin, "D") <> 0 Then DestSub = GetDestSub(Origin) Else DestSub = SourceSub
  V1Type = TypeOfValue(V1, Subroutine(SourceSub))
  V2Type = TypeOfValue(V2, Subroutine(SourceSub))
  DestType = TypeOfVar(Answer, Subroutine(DestSub))
  CalcType = DestType
  If CastOrder(V1Type) > CastOrder(CalcType) Then CalcType = V1Type
  If CastOrder(V2Type) > CastOrder(CalcType) Then CalcType = V2Type
  V1 = DelType(V1): V2 = DelType(V2)
  V1O = V1: V2O = V2

  'Remove cast from output var
  Answer = DelType(Answer)

  'Get output var
  If DestType = CalcType Then
    AV = Answer
  Else
    AV = GetCalcVar(CalcType)
  End If

  'Generate asm code for sum
  'Check if both are constants
  IF IsConst(V1) AND IsConst(V2) And INSTR(V1, "@") = 0 AND INSTR(V2, "@") = 0 THEN
    If IsIntType(CalcType) Then
      IF Act = "*" THEN AV = Str(MakeDec(V1) * MakeDec(V2))
      IF Act = "/" THEN AV = Str(INT(MakeDec(V1) / MakeDec(V2)))
      IF Act = "%" THEN AV = Str(MakeDec(V1) MOD MakeDec(V2))
    ElseIf IsFloatType(CalcType) Then
      IF Act = "*" THEN AV = Str(MakeDecFloat(V1) * MakeDecFloat(V2))
      IF Act = "/" THEN AV = Str(MakeDecFloat(V1) / MakeDecFloat(V2))
      IF Act = "%" THEN AV = Str(MakeDecFloat(V1) MOD MakeDecFloat(V2))
    End If
    GOTO MultDivAnswer
  END IF

  'If multiply, make sure V2 is the constant
  If Act = "*" And IsConst(V1) And (Not IsConst(V2)) Then
    Swap V1, V2
    Swap V1O, V2O
    Swap V1Type, V2Type
  End If

  'Simplifications
  If Act = "*" And V2 = "0" Then AV = "0": Goto MultDivAnswer
  If (Act = "*" OR Act = "/") And V2 = "1" Then AV = V1: Goto MultDivAnswer
  If (Act = "/" Or Act = "%") And V1 = "0" Then AV = "0": Goto MultDivAnswer

  'Use hardware multiply instruction for byte*byte multiply
  If CalcType = "BYTE" And Act = "*" And ChipFamily = 16 Then
    If IsConst(V1) Then
      CurrLine = LinkedListInsert(CurrLine, " movlw " + Str(MakeDec(V1)))
    Else
      CurrLine = LinkedListInsert(CurrLine, " movf " + V1 + ",W")
    End If
    If IsConst(V2) Then
      CurrLine = LinkedListInsert(CurrLine, " mullw " + Str(MakeDec(V2)))
    Else
      CurrLine = LinkedListInsert(CurrLine, " mulwf " + V2)
    End If
    AV = "PRODL"
    Goto MultDivAnswer
  End If

  'Rotate left/right for mult or div by 2
  IF CalcType = "BYTE" And MakeDec(V2) = 2 Then

    AddVar V1, "BYTE", 1, 0, "REAL", Origin
    If ModeAVR Then
      Temp = ""
      If Act = "%" THEN Temp = "H:"
      R1 = PutInRegister(NewCode, "O:" + Temp + V1, CalcType, Origin)
      CurrLine = LinkedListInsertList(CurrLine, NewCode)
    End If

    If Act = "*" Then
      If ModePIC Then
        CurrLine = LinkedListInsert(CurrLine, " bcf STATUS,C")
        If V1 = Answer Then
          CurrLine = LinkedListInsert(CurrLine, " rlf " + V1 + ",F")
        Else
          CurrLine = LinkedListInsert(CurrLine, " rlf " + V1 + ",W")
          CurrLine = LinkedListInsert(CurrLine, " movwf " + Answer)
        End If
        AV = Answer
      ElseIf ModeAVR Then
        CurrLine = LinkedListInsert(CurrLine, " lsl " + R1)
        AV = R1
      End If

    ElseIf Act = "/" Then
      If ModePIC Then
        CurrLine = LinkedListInsert(CurrLine, " bcf STATUS,C")
        If V1 = Answer Then
          CurrLine = LinkedListInsert(CurrLine, " rrf " + V1 + ",F")
        Else
          CurrLine = LinkedListInsert(CurrLine, " rrf " + V1 + ",W")
          CurrLine = LinkedListInsert(CurrLine, " movwf " + Answer)
        End If
        AV = Answer
      ElseIf ModeAVR Then
        CurrLine = LinkedListInsert(CurrLine, " lsr " + R1)
        AV = R1
      End If

    ElseIf Act = "%" Then
      If ModePIC Then
        CurrLine = LinkedListInsert(CurrLine, " clrf " + Answer)
        ILC += 1
        CurrLine = LinkedListInsert(CurrLine, " btfss " + V1 + ",0")
        CurrLine = LinkedListInsert(CurrLine, " goto ENDIF" + Str(ILC))
        CurrLine = LinkedListInsert(CurrLine, " incf " + Answer + ",F")
        CurrLine = LinkedListInsert(CurrLIne, "ENDIF" + Str(ILC))
        AV = Answer
      ElseIf ModeAVR Then
        CurrLine = LinkedListInsert(CurrLine, " andi " + R1 + ",1")
        AV = R1
      End If
    End If
    Goto MultDivAnswer
  End If

  'Declare variables needed for calculation
  AddVar "Sys" + CalcType + "TempA", CalcType, 1, 0, "REAL", Origin, , -1
  AddVar "Sys" + CalcType + "TempB", CalcType, 1, 0, "REAL", Origin, , -1
  AddVar "Sys" + CalcType + "TempX", CalcType, 1, 0, "REAL", Origin, , -1
  IF Act = "/" OR Act = "%" THEN AddVar "SysDivLoop", "BYTE", 1, 0, "REAL", Origin, , -1

  'Copy parameters
  CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V1, "Sys" + CalcType + "TempA", Origin))
  CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V2, "Sys" + CalcType + "TempB", Origin))

  'Use hardware multiply instruction for byte*byte multiply
  If CalcType = "BYTE" And Act = "*" And ModeAVR And HMult Then
    CurrLine = LinkedListInsert(CurrLine, " mul SysByteTempA,SysByteTempB")
    AV = "SysByteTempX"
    Goto MultDivAnswer
  End If

  'Decide which Sub to call
  IF Act = "*" THEN SNT = "SysMultSub"
  IF Act = "/" THEN SNT = "SysDivSub"
  IF Act = "%" THEN SNT = "SysDivSub"
  IF CalcType = "WORD" THEN SNT += "16"
  IF CalcType = "INTEGER" THEN SNT += "INT"
  If CalcType = "LONG" Then SNT += "32"
  IF CalcType = "SINGLE" THEN SNT += "SINGLE"
  IF CalcType = "DOUBLE" THEN SNT += "DOUBLE"
  IF CalcType = "ULONGINT" THEN SNT += "64"

  'Special case for SINGLE
  If Act = "%" and CalcType = "SINGLE" Then SNT = "SYSMODSUBSINGLE"
  If Act = "%" and CalcType = "DOUBLE" Then SNT = "SYSMODSUBDOUBLE"
  

  'Call calculation sub
  SNT = Ucase( SNT )  'added was case sensistive
  CurrLine = LinkedListInsert(CurrLine, " call " + SNT)
  RequestSub (Subroutine(SourceSub), SNT) 'Mark as used

  'Write answer
  If Act = "*" Then AV = "Sys" + CalcType + "TempX"
  If Act = "/" Then AV = "Sys" + CalcType + "TempA"
  If Act = "%" Then AV = "Sys" + CalcType + "TempX"
  
  If (( compilerdebug and cCOMPILECALCMULT ) = cCOMPILECALCMULT ) Then
    Print " 8 COMPILECALCMULT :" + V1, V2, AV, CalcType
  End If    

  'Replace sum with answer variable
 MultDivAnswer:
  OutList->CodeEnd = CurrLine
  Return AV
  
  End Function

Function CompileCalcUnary(OutList As CodeSection Pointer, Act As String, V2 As String, Origin As String, Answer As String) As String

  Dim As String V1O, V2O, V1Type, V2Type, CalcType, DestType, AV, R1, R2, AVH, SNT
  Dim As String Temp, Ovr, OldCalcType
  Dim As Integer SourceSub, DestSub, CD, CurrVarByte
  Dim As LinkedListElement Pointer CurrLine, NewCode
  CurrLine = OutList->CodeEnd

  'Delete casts (not needed for logic operations)
  If Act = "!" Then V2 = DelType(V2)

  'Get types
  SourceSub = GetSubID(Origin)
  If INSTR(Origin, "D") <> 0 Then DestSub = GetDestSub(Origin) Else DestSub = SourceSub
  V2Type = TypeOfValue(V2, Subroutine(SourceSub))
  DestType = TypeOfVar(Answer, Subroutine(DestSub))
  CalcType = DestType
  V2 = DelType(V2)

  'Remove cast from output var
  Answer = DelType(Answer)

  'If op is negate, return an integer
  If Act = "-" And CastOrder(CalcType) < CastOrder("INTEGER") Then CalcType = "INTEGER"

  'Check types
  If V2Type = "STRING" Or (V2Type = "SINGLE" and Act = "!") Then
    Temp = Message("OperandTypeMismatch")
    Replace Temp, "%operand%", Act
    Replace Temp, "%type%", V2Type
    LogError Temp, Origin
  End If

  'Get output var
  AV = Answer
  OldCalcType = CalcType

  'Deal with bit variable input
  'Convert bit vars to byte
  If V2Type = "BIT" And Not IsConst(V2) Then
    'If answer is also a bit and operation is NOT, use copy with invert
    If DestType = "BIT" And Act = "!" And V2 <> AV Then
      CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V2, AV, Origin, -1))
      OutList->CodeEnd = CurrLine
      Return AV
    Else
      If V2 = AV Then
        V2Type = "BYTE"
        V2O = V2
        If IsSysTemp(AV) Then
          V2 = AV
        Else
          V2 = GetCalcVar("BYTE")
          AV = V2
        End If
        DestType = "BYTE"
        CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V2O, V2, Origin))
      Else
        V2Type = "BYTE"
        V2O = V2
        If IsSysTemp(AV) Then
          V2 = AV
        Else
          V2 = GetCalcVar("BYTE")
        End If
        CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V2O, V2, Origin))
      End If
    End If
  End If

  'If V2 is constant, produce constant result
  If IsConst(V2) And InStr(V2, "@") = 0 Then
    IF Act = "!" THEN
      If CalcType = "BYTE" Then Return Trim(Str((NOT MakeDec(V2)) AND 255))
      If CalcType = "WORD" OR CalcType = "INTEGER" Then Return Trim(Str((Not MakeDec(V2) And 65535)))
      If CalcType = "LONG" Then Return Trim(Str(NOT MakeDec(V2)))
    ElseIf Act = "-" THEN
      'Do nothing
      Return "-" + V2
    End If
  End If

  'On AVR, need to do negate on entire variable at once
  If ModeAVR And Act = "-" Then

    'Put value in register
    'Overwrite V2?
    Ovr = "O:"
    If UCase(Left(V2, 7)) = "SYSTEMP" Then Ovr = ""
    If UCase(Left(V2, 11)) = "SYSCALCTEMP" Then Ovr = ""

    NewCode = LinkedListCreate
    R2 = PutInRegister(NewCode, Ovr + V2, CalcType, Origin)
    AddVar V2, "BYTE", 1, Subroutine(SourceSub), "REAL", Origin
    CurrLine = LinkedListInsertList(CurrLine, NewCode)

    'Perform operation
    For CurrVarByte = 0 To GetTypeSize(CalcType) - 1
      CurrLine = LinkedListInsert(CurrLine, " com " + GetByte(R2, CurrVarByte))
    Next

    CurrLine = LinkedListInsert(CurrLine, " inc " + GetByte(R2, 0))
    For CurrVarByte = 1 To GetTypeSize(CalcType) - 1
      CurrLine = LinkedListInsert(CurrLine, " brne PC + 2")
      CurrLine = LinkedListInsert(CurrLine, " inc " + GetByte(R2, CurrVarByte))
    Next

    'Copy result to output variable
    If R2 <> AV Then
      NewCode = CompileVarSet(R2, AV, Origin)
      CurrLine = LinkedListInsertList(CurrLine, NewCode)
      FreeCalcVar R2
    End If

  Else

    'Process each byte of variable one at a time
    For CurrVarByte = 0 To GetTypeSize(CalcType) - 1

      'NOT (Byte and Word)

      'No matching byte in input, so set output byte to 255
      If CurrVarByte >= GetTypeSize(V2Type) Then
        If ModePIC Then
          If ChipFamily <> 16 Then
            CurrLine = LinkedListInsert(CurrLine, " movlw 255")
            CurrLine = LinkedListInsert(CurrLine, " movwf " + GetByte(AV, CurrVarByte))
          End If
          If ChipFamily = 16 Then CurrLine = LinkedListInsert(CurrLine, " setf " + GetByte(AV, CurrVarByte))
        ElseIf ModeAVR Then
          'R2 = GetCalcVar("BYTE")
          'CurrLine = LinkedListInsert(CurrLine, " clr " + R2)
          'CurrLine = LinkedListInsert(CurrLine, " com " + R2)
          R2 = "255"
        End If

      'Matching byte in input for current output byte, so invert input
      Else
        If ModePIC Then
          If AV <> V2 Then
            CurrLine = LinkedListInsert(CurrLine, " comf " + GetByte(V2, CurrVarByte) + ",W")
            CurrLine = LinkedListInsert(CurrLine, " movwf " + GetByte(AV, CurrVarByte))
            If CurrVarByte = 0 Then
              AddVar V2, "BYTE", 1, Subroutine(SourceSub), "REAL", Origin
              AddVar AV, "BYTE", 1, Subroutine(DestSub), "REAL", Origin
            End If
          Else
            CurrLine = LinkedListInsert(CurrLine, " comf " + GetByte(V2, CurrVarByte) + ",F")
            If CurrVarByte = 0 Then
              AddVar V2, "BYTE", 1, Subroutine(SourceSub), "REAL", Origin
            End If
          End If

        ElseIf ModeAVR Then
          'If AVR, put current byte in register
          'Overwrite V2?
          Ovr = "O:"
          If UCase(Left(V2, 7)) = "SYSTEMP" Then Ovr = ""
          If UCase(Left(V2, 11)) = "SYSCALCTEMP" Then Ovr = ""

          NewCode = LinkedListCreate
          R2 = PutInRegister(NewCode, Ovr + GetByte(V2, CurrVarByte), "BYTE", Origin)
          AddVar V2, "BYTE", 1, Subroutine(SourceSub), "REAL", Origin
          CurrLine = LinkedListInsertList(CurrLine, NewCode)

          'Perform operation
          CurrLine = LinkedListInsert(CurrLine, " com " + R2)

        End If
      End If

      If ModeAVR Then
        'Copy result to output variable
        If OldCalcType = "BIT" And R2 <> Answer Then
          NewCode = CompileVarSet(R2, Answer, Origin)
          CurrLine = LinkedListInsertList(CurrLine, NewCode)
          FreeCalcVar R2
          AV = Answer
        ElseIf AV <> R2 Then
          NewCode = CompileVarSet(R2, "[byte]" + GetByte(AV, CurrVarByte), Origin)
          CurrLine = LinkedListInsertList(CurrLine, NewCode)
          FreeCalcVar R2
        End If
      End If

    Next

    If Act = "-" And CalcType = "INTEGER" Or CalcType = "LONGINT" Then
      '(PIC only, can't reach here on AVR)
      CurrLine = LinkedListInsert(CurrLine, " incf " + GetByte(AV, 0) + ",F")
      For CurrVarByte = 1 To GetTypeSize(CalcType) - 1
        CurrLine = LinkedListInsert(CurrLine, " btfsc STATUS,Z")
        CurrLine = LinkedListInsert(CurrLine, " incf " + GetByte(AV, CurrVarByte) + ",F")
      Next
    End If

  End If

  OutList->CodeEnd = CurrLine
  Return AV
  
  End Function

Function CompileConditions (Condition As String, IfTrue As String, Origin As String, CompSub As SubType Pointer = 0) As LinkedListElement Pointer

  Dim As String V1, V2, Op, R1, R2, Temp, Cmd, BI, VarName, Sum, Reg, CondType
  Dim As String BitName, VarBit
  Dim As Integer PD, Complex, Value1, Value2, OutVal, R2Literal, T, S, WD
  Dim As Integer IsBitTest, IsInverted, BitAndValue
  Dim As SubType Pointer CurrSub
  Dim As LinkedListElement Pointer OutList, CurrLine, NewCode
  Dim As VariableType Pointer FoundUserVar

  'If Condition = IfTrue, execute next instruction

  'Initialise
  OutList = LinkedListCreate
  CurrLine = OutList

  'Get origin
  'Origin = ""
  IF INSTR(Condition, ";?F") <> 0 THEN
    Origin = Mid(Condition, INSTR(Condition, ";?F"))
    Condition = Left(Condition, INSTR(Condition, ";?F") - 1)
  END If
  Condition = Trim(Condition)
  If CompSub <> 0 Then
    CurrSub = CompSub
  Else
    CurrSub = Subroutine(GetSubID(Origin))
  End If
  'IF Origin = "" THEN PRINT "No Origin!", Condition

  'Trim extra brackets on Condition
  If Left(Condition, 1) = "(" And Right(Condition, 1) = ")" Then
    If CountOccur(Condition, "';()") = 2 Then
      Condition = Trim(Mid(Condition, 2, Len(Condition) - 2))
    End If
  End If

  'Decide whether to compile inline or using CompileCalc
  'Complex =
  ' 0: One condition, byte, compile inline
  ' 1: More than one condition or not byte, so compile as calculation
  ' -1: No condition, single variable, check if 0 or not 0.

  CheckConditionAgain:
  Complex = 0
  CondType = TypeOfValue(Condition, Subroutine(GetSubID(Origin)))
  IF CountOccur(Condition, "';=~<>{}") = 0 THEN Complex = -1
  IF CountOccur(Condition, "';=~<>{}+-*/%&|#!") >= 2 THEN Complex = 1
  IF CountOccur(Condition, "';+-*/%&|#!") >= 1 THEN Complex = 1
  If CondType <> "BIT" And CondType <> "BYTE" THEN Complex = 1
  'PRINT Condition, GetSub(Origin), CondType, Complex

  'No conditions - check if variable is 0 or non-0
  'NOT is not taken into account above, need to detect it here
  IF Complex = -1 THEN
    If IsConst(Condition) Then
      If MakeDec(Condition) = 0 Then
        Condition = "AlwaysFalse"
      Else
        Condition = "AlwaysTrue"
      End If
      Return OutList
    Else
      If CondType = "BYTE" Then

        IsInverted = 0
        If Left(Condition, 1) = "!" Then
          Condition = Trim(Mid(Condition, 2))
          IsInverted = -1
        End If

        If ModePIC Then
          CurrLine = LinkedListInsert(CurrLine, " movf " + Condition + ",F")
          If IsInverted Then
            IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN Cmd = " btfsc STATUS,Z"
            IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN Cmd = " btfss STATUS,Z"
          Else
            IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN Cmd = " btfss STATUS,Z"
            IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN Cmd = " btfsc STATUS,Z"
          End If
          CurrLine = LinkedListInsert(CurrLine, Cmd)
        ElseIf ModeAVR Then
          NewCode = LinkedListCreate
          Reg = PutInRegister(NewCode, Condition, "BYTE", Origin)
          CurrLine = LinkedListInsertList(CurrLine, NewCode)
          CurrLine = LinkedListInsert(CurrLine, " tst " + Reg)
          If IsInverted Then
            IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN Cmd = " brne PC + 2"
            IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN Cmd = " breq PC + 2"
          Else
            IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN Cmd = " breq PC + 2"
            IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN Cmd = " brne PC + 2"
          End If
          CurrLine = LinkedListInsert(CurrLine, Cmd)
        End If

      Else
        'Checking a single bit
        'Can do this just as efficiently using bit test code in section below
        If InStr(Condition, " ") = 0 Then Condition = Condition + "=1"
        Complex = 0
      End If
    End If
  END If

  'One condition and byte, so compile inline
  IF Complex = 0 THEN
    OP = ""
    If INSTR(Condition, "=") <> 0 THEN OP = "="
    IF INSTR(Condition, "~") <> 0 THEN OP = "~"
    IF INSTR(Condition, "<") <> 0 THEN OP = "<"
    IF INSTR(Condition, ">") <> 0 THEN OP = ">"
    IF INSTR(Condition, "{") <> 0 THEN OP = "{"
    IF INSTR(Condition, "}") <> 0 THEN OP = "}"

    'Read input variables
    If OP = "" Then
      V1 = UCase(Left(Condition, INSTR(Condition, " ") - 1))
      V2 = UCase(Mid(Condition, INSTR(Condition, " ") + 1))
      OP = "="
    Else
      V1 = UCase(Left(Condition, INSTR(Condition, OP) - 1))
      DO WHILE INSTR(V1, " ") <> 0: Replace V1, " ", "": LOOP
      V2 = UCase(Mid(Condition, INSTR(Condition, OP) + 1))
      DO WHILE INSTR(V2, " ") <> 0: Replace V2, " ", "": Loop
    End If

    IsBitTest = 0
    If (TypeOfValue(V1, CurrSub) = "BIT" Or IsConst(V1)) And (TypeOfValue(V2, CurrSub) = "BIT" Or IsConst(V2)) Then
      IsBitTest = -1
    End If
    'Print Condition, ":"; V1, OP, V2, IsBitTest

    'If both V1 and V2 are constants, evaluate condition now
    If IsConst(V1) And IsConst(V2) Then
      Value1 = MakeDec(V1)
      Value2 = MakeDec(V2)
      OutVal = 0
      Select Case OP
      Case "=": OutVal = Value1 = Value2
      Case "~": OutVal = Value1 <> Value2
      Case "<": OutVal = Value1 < Value2
      Case ">": OutVal = Value1 > Value2
      Case "{": OutVal = Value1 <= Value2
      Case "}": OutVal = Value1 >= Value2
      End Select

      If OutVal Then
        Condition = "AlwaysTrue"
      Else
        Condition = "AlwaysFalse"
      End If

      Return OutList
    End If

    'Code for a bit test
    IF IsBitTest THEN
      'If reading from a pin, record it
      Dim As PinDirType Pointer CurrPinDir
      CurrPinDir = GetPinDirection(V1)
      If CurrPinDir <> 0 Then
        CurrPinDir->ReadFrom = -1
      End If

      'If V1 is const and V2 is not, swap
      If IsConst(V1) And Not IsConst(V2) Then
        Swap V1, V2
      End If

      'Compare bit variable to bit constant
      If IsConst(V2) Then
        Dim As String BitTestTemp = "SysBitTest"

        T = 1
        'Get var and bit
        BI = FixBit(V1, Origin)
        If INSTR(BI, ".") <> 0 Then Replace BI, ".", ","
        'Get state to test
        S = 1: If V2 = "0" THEN S = 0
        If OP = "~" Or OP = ">" Or OP = "<" Then S = 1 - S

        'Is bit referenced by byte?
        'If no var, might be dealing with an SFR bit
        BitName = V1
        If InStr(BitName, ".") = 0 Then
          BitName = GetWholeSFR(BitName)
        End If
        'Should now have a var!
        If InStr(BitName, ".") <> 0 Then
          VarName = Left(BitName, InStr(BitName, ".") - 1)
          VarBit = Mid(BitName, InStr(BitName, ".") + 1)
        Else
          'This is an error
          Temp = Message("InvalidBitRead")
          Replace Temp, "%bit%", V1
          LogError(Temp, Origin)
          VarBit = "0"
        End If

        If Not IsConst(VarBit) And Not HasSFRBit(VarBit) Then
          'Bit number isn't a number or SFR, must be referenced by variable
          'Get maximum allowed bit number
          BitAndValue = GetTypeSize(TypeOfVar(VarName, CurrSub)) * 8 - 1

          'Variable to operate on specified by FSR (or FSR0) on PIC or SysReadA on AVR
          'Bit to operate on comes from SysByteTempX
          'New state of bit is in bit 0 of SysByteTempB
          If ChipFamily = 12 Or ChipFamily = 14 Then
            CurrLine = LinkedListInsert(CurrLine, " movlw low " + VarName)
            CurrLine = LinkedListInsert(CurrLine, " movwf FSR")
            If HighFSR Then CurrLine = LinkedListInsert(CurrLine, " bankisel " + VarName)

          ElseIf ChipFamily = 15 Then
            CurrLine = LinkedListInsert(CurrLine, " movlw low " + VarName)
            CurrLine = LinkedListInsert(CurrLine, " movwf FSR0L")
            CurrLine = LinkedListInsert(CurrLine, " movlw high " + VarName)
            CurrLine = LinkedListInsert(CurrLine, " movwf FSR0H")

          ElseIf ChipFamily = 16 Then
            CurrLine = LinkedListInsert(CurrLine, " lfsr 0," + VarName)

          End If

          If ModePIC Then
            CurrLine = LinkedListInsert(CurrLine, " movlw " + Str(BitAndValue))
            CurrLine = LinkedListInsert(CurrLine, " andwf " + VarBit + ", W")
            CurrLine = LinkedListInsert(CurrLine, " movwf SysByteTempX")
            CurrLine = LinkedListInsert(CurrLine, " call SysReadBit")
            BI = "STATUS,Z" ' Code below can do checking of Z. Need to invert S, Z is set when test bit clear
            S = 1 - S
            RequestSub(CurrSub, "SysReadBit")
            If Not IsIOReg(VarName) Then AddVar VarName, "BYTE", 1, CurrSub, "REAL", Origin

          ElseIf ModeAVR Then
            VarName = GetRealIOName(VarName)
            CurrLine = LinkedListInsert(CurrLine, " ldi SysStringA, low(" + VarName + ")")
            CurrLine = LinkedListInsert(CurrLine, " ldi SysStringA_H, high(" + VarName + ")")

            CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(VarBit, "SysReadA", Origin))
            CurrLine = LinkedListInsert(CurrLine, " andi SysReadA, " + Str(BitAndValue))

            CurrLine = LinkedListInsert(CurrLine, " rcall SysReadBit")
            BI = "SREG,Z"
            S = 1 - S

            RequestSub(CurrSub, "SysReadBit")
            If Not IsIOReg(VarName) Then AddVar VarName, "BYTE", 1, CurrSub, "REAL", Origin
            AddVar "SysStringA", "WORD", 1, 0, "REAL", Origin, , -1 'Needs to be global

          End If
        End If

        If ModePIC Then
          IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN
            Cmd = " btfss "
            IF S = 1 THEN Cmd = " btfsc "
          END IF
          IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN
            Cmd = " btfsc "
            IF S = 1 THEN Cmd = " btfss "
          END IF

          CurrLine = LinkedListInsert(CurrLine, Cmd + BI)
          If INSTR(BI, ",") <> 0 Then
            VarName = Left(BI, INSTR(BI, ",") - 1)
            AddVar VarName, "BYTE", 1, CurrSub, "REAL", Origin
          End If
        ElseIf ModeAVR Then
          VarName = GetRealIOName(Left(BI, INSTR(BI, ",") - 1))

          If ChipAVRDX Then
            If IsIORegDX(VarName) Then
              'Map legacy megaAVR register to DX register using the Alias Addressing
              If (( compilerdebug and cAVRDXDEBUG ) = cAVRDXDEBUG )  Then
                CurrLine = LinkedListInsert(CurrLine, ";IOAVRDX #3")
              End if
              VarName = GetSysVarAliasName ( VarName ) 
            End If          
          End If

          BI = Mid(BI, INSTR(BI, ",") + 1)
          If VarName = "SREG" Then
            IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN
              Cmd = " brbs "
              IF S = 1 THEN Cmd = " brbc "
            END IF
            IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN
              Cmd = " brbc "
              IF S = 1 THEN Cmd = " brbs "
            END IF
            CurrLine = LinkedListInsert(CurrLine, Cmd + BI + ",PC + 2")

          ElseIf IsRegister(VarName) Then
            IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN
              Cmd = " sbrs "
              IF S = 1 THEN Cmd = " sbrc "
            END IF
            IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN
              Cmd = " sbrc "
              IF S = 1 THEN Cmd = " sbrs "
            END IF
            CurrLine = LinkedListInsert(CurrLine, Cmd + VarName + "," + BI)

          ElseIf IsLowIOReg(VarName) Then
          
            IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN
              Cmd = " sbis "
              IF S = 1 THEN Cmd = " sbic "
            END IF
            IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN
              Cmd = " sbic "
              IF S = 1 THEN Cmd = " sbis "
            END IF
            CurrLine = LinkedListInsert(CurrLine, Cmd + VarName + "," + BI)

          ElseIf IsIOReg(VarName) Then
            AddVar BitTestTemp, "BYTE", 1, CurrSub, "REAL", Origin, , -1
            CurrLine = LinkedListInsert(CurrLine, " in " + BitTestTemp + "," + VarName)
            IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN
              Cmd = " sbrs " + BitTestTemp + ","
              IF S = 1 THEN Cmd = " sbrc " + BitTestTemp + ","
            ElseIF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN
              Cmd = " sbrc " + BitTestTemp + ","
              IF S = 1 THEN Cmd = " sbrs " + BitTestTemp + ","
            END IF
            CurrLine = LinkedListInsert(CurrLine, Cmd + BI)

          Else
            AddVar BitTestTemp, "BYTE", 1, CurrSub, "REAL", Origin, , -1
            CurrLine = LinkedListInsert(CurrLine, " lds " + BitTestTemp + "," + VarName)
            IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN
              Cmd = " sbrs " + BitTestTemp + ","
              IF S = 1 THEN Cmd = " sbrc " + BitTestTemp + ","
            ElseIF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN
              Cmd = " sbrc " + BitTestTemp + ","
              IF S = 1 THEN Cmd = " sbrs " + BitTestTemp + ","
            END IF
            CurrLine = LinkedListInsert(CurrLine, Cmd + BI)
            AddVar VarName, "BYTE", 1, CurrSub, "REAL", Origin

          End If
        End If

      'Need to compare 2 bit variables
      Else
        'Check operator
        If Op <> "=" And Op <> "~" Then
          LogError(Message("BadBitCompareOp"), Origin)
        End If

        If ModePIC Then
          'Is indirect mode needed?
          R1 = V1
          If InStr(V1, ".") <> 0 Then R1 = Mid(V1, InStr(V1, ".") + 1)
          R2 = V2
          If InStr(V2, ".") <> 0 Then R2 = Mid(V2, InStr(V2, ".") + 1)
          If (Not IsConst(R1) And Not HasSFRBit(R1)) Or (Not IsConst(R2) And Not HasSFRBit(R2)) Then
            'one or both variables use indirect mode
            CurrLine = LinkedListInsert(CurrLine, " clrf SysByteTempB")
            CurrLine = LinkedListInsertList(CurrLine, CompileConditions(V1 + "=1", "TRUE", Origin, CurrSub))
            CurrLine = LinkedListInsert(CurrLine, " comf SysByteTempB,F")
            CurrLine = LinkedListInsertList(CurrLine, CompileConditions(V2 + "=1", "TRUE", Origin, CurrSub))
            CurrLine = LinkedListInsert(CurrLine, " comf SysByteTempB,F")
            AddVar "SysByteTempB", "BYTE", 1, CurrSub, "REAL", Origin, , -1

          Else
            'Get variables
            R1 = FixBit(V1, Origin)
            If INSTR(R1, ".") <> 0 Then Replace R1, ".", ","
            R2 = FixBit(V2, Origin)
            If INSTR(R2, ".") <> 0 Then Replace R2, ".", ","

            'Add to resolve undeclared variable, as an undeclared variable causes ASM error and the memory has not be allocated
            FoundUserVar =  HashMapGet( @(Subroutine(0)->Variables) , Left(R2,Instr(R2,",")-1) )
            If FoundUserVar = 0 Then
              'Create a variable but do not mark as used - this will then throw an Explict error as expected
              AddVar Left(R2,Instr(R2,",")-1), TypeOfValue( Left(R2,Instr(R2,",")-1) , CurrSub), 1, CurrSub, "REAL", Origin, -1 , 0
            Else
              'variable has been located just validate its use
              AddVar Left(R2,Instr(R2,",")-1), TypeOfValue( Left(R2,Instr(R2,",")-1) , CurrSub), 1, CurrSub, "REAL", Origin, -1 , -1
            End If

            'clear w, xor if each var set
            CurrLine = LinkedListInsert(CurrLine, " clrw")
            CurrLine = LinkedListInsert(CurrLine, " btfsc " + R1)
            CurrLine = LinkedListInsert(CurrLine, " xorlw 255")
            CurrLine = LinkedListInsert(CurrLine, " btfsc " + R2)
            CurrLine = LinkedListInsert(CurrLine, " xorlw 255")
          End If
          'If W is 0, variables are equal (inverted 0 or 2 times)
          If Op = "=" Then
            IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN Cmd = " btfsc STATUS,Z"
            If INSTR(UCase(IfTrue), "FALSE") <> 0 THEN Cmd = " btfss STATUS,Z"
          Else
            IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN Cmd = " btfss STATUS,Z"
            If INSTR(UCase(IfTrue), "FALSE") <> 0 THEN Cmd = " btfsc STATUS,Z"
          End If
          CurrLine = LinkedListInsert(CurrLine, Cmd)

        ElseIf ModeAVR Then
          'Use recursion, easier!
          'Otherwise, same algorithm as PIC: clr, if, com, if, com, test
          'clear w, xor if each var set
          AddVar "SysCalcTempX", "BYTE", 1, CurrSub, "REAL", Origin, , -1
          CurrLine = LinkedListInsert(CurrLine, " clr SysCalcTempX")
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(V1 + "=1", "TRUE", Origin, CurrSub))
          CurrLine = LinkedListInsert(CurrLine, " com SysCalcTempX")
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(V2 + "=1", "TRUE", Origin, CurrSub))
          CurrLine = LinkedListInsert(CurrLine, " com SysCalcTempX")
          'If W is 0, variables are equal (inverted 0 or 2 times)
          If Op = "=" Then
            IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN Cmd = " sbrs SysCalcTempX,0"
            If INSTR(UCase(IfTrue), "FALSE") <> 0 THEN Cmd = " sbrc SysCalcTempX,0"
          Else
            IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN Cmd = " sbrc SysCalcTempX,0"
            If INSTR(UCase(IfTrue), "FALSE") <> 0 THEN Cmd = " sbrs SysCalcTempX,0"
          End If
          CurrLine = LinkedListInsert(CurrLine, Cmd)

        End If

      End If

    'If the condition is not a bit test, then run this code:
    Else
      'Add needed variables
      'Generate warning if comparing bit variable to other type of variable
      'If using PIC and a bit variable is found, put bit variable into temporary variable
      If Not IsConst(V1) Then
        If TypeOfValue(V1, CurrSub) = "BIT" Then
          Temp = Message("WarningBitByteCompare")
          Replace Temp, "%nonbit%", V2
          LogWarning(Temp, Origin)
          If ModePIC Then
            AddVar "SYSCALCTEMPA", "BYTE", 1, CurrSub, "REAL", Origin, , -1
            CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V1, "SysCalcTempA", Origin))
            V1 = "SysCalcTempA"
          End If
        End If
        AddVar V1, "BYTE", 1, CurrSub, "REAL", Origin
      End If
      If Not IsConst(V2) Then
        If TypeOfValue(V2, CurrSub) = "BIT" Then
          Temp = Message("WarningBitByteCompare")
          Replace Temp, "%nonbit%", V1
          LogWarning(Temp, Origin)

          If ModePIC Then
            AddVar "SYSCALCTEMPB", "BYTE", 1, CurrSub, "REAL", Origin, , -1
            CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V2, "SysCalcTempB", Origin))
            V2 = "SysCalcTempB"
          End If

        End If
        AddVar V2, "BYTE", 1, CurrSub, "REAL", Origin
      End If

      'Remove any casts
      If InStr(V1, "]") <> 0 Then
        V1 = Trim(Mid(V1, InStr(V1, "]") + 1))
      End If
      If InStr(V2, "]") <> 0 Then
        V2 = Trim(Mid(V2, InStr(V2, "]") + 1))
      End If

      'Code to run comparison
      'Used by all AVR compare modes
      If ModeAVR Then
        'Swap V1/V2 as needed
        IF ((OP = "=" Or OP = "~") And IsConst(V1)) OR OP = ">" OR OP = "{" Then
          Swap V1, V2
        End If

        'Put V1 in SysCalcTempA
        If IsRegister(V1) Then
          R1 = V1
        Else
          R1 = "SysCalcTempA"
          AddVar R1, "BYTE", 1, CurrSub, "REAL", Origin, , -1
          CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(V1, "SysCalcTempA", Origin))
        End If
        'ElseIf IsIOReg(V1) Then
        ' R1 = "SysCalcTempA"
        ' CurrLine = LinkedListInsert(CurrLine, " in SysCalcTempA," + V1)
        'ElseIf IsConst(V1) Then
        ' R1 = "SysCalcTempA"
        ' CurrLine = LinkedListInsert(CurrLine, " ldi SysCalcTempA," + Str(MakeDec(V1)))
        'Else
        ' R1 = "SysCalcTempA"
        ' CurrLine = LinkedListInsert(CurrLine, " lds SysCalcTempA," + V1)
        ' AddVar V1, "BYTE", 1, 0, "REAL", Origin
        'End If

        'Put V2 in SysCalcTempB
        R2Literal = 0
        If IsRegister(V2) Then
          R2 = V2
        ElseIf IsIOReg(V2) Then
          R2 = "SysCalcTempB"
          AddVar R2, "BYTE", 1, CurrSub, "REAL", Origin, , -1
          CurrLine = LinkedListInsert(CurrLine, " in SysCalcTempB," + V2)
        ElseIf IsConst(V2) Then
          R2 = Str(MakeDec(V2))
          R2Literal = -1
        Else
          R2 = "SysCalcTempB"
          AddVar R2, "BYTE", 1, CurrSub, "REAL", Origin, , -1
          CurrLine = LinkedListInsert(CurrLine, " lds SysCalcTempB," + V2)
          AddVar V2, "BYTE", 1, CurrSub, "REAL", Origin
        End If

        'Compare
        If R2Literal Then
          If Val(R2) = 0 And (OP = "=" Or OP = "~") Then
            CurrLine = LinkedListInsert(CurrLine, " tst " + R1)
          Else
            CurrLine = LinkedListInsert(CurrLine, " cpi " + R1 + "," + R2)
          End If
        Else
          CurrLine = LinkedListInsert(CurrLine, " cp " + R1 + "," + R2)
        End If

      End If

      'Code for equal/not equal
      IF OP = "=" OR OP = "~" THEN

        If ModePIC Then
          'V1 must always be a variable. V2 can be variable or constant.
          IF IsConst(V1) Then
            Temp = V1
            V1 = V2
            V2 = Temp
          End If

          If IsConst(V2) And MakeDec(V2) = 0 Then
            CurrLine = LinkedListInsert(CurrLine, " movf " + V1 + ",F")
          ElseIf IsConst(V2) And MakeDec(V2) = 1 Then
            CurrLine = LinkedListInsert(CurrLine, " decf " + V1 + ",W")
          ElseIf IsConst(V2) And MakeDec(V2) = 255 Then
            CurrLine = LinkedListInsert(CurrLine, " incf " + V1 + ",W")
          Else
            IF IsConst(V2) Then
              CurrLine = LinkedListInsert(CurrLine, " movlw " + Str(MakeDec(V2)))
            Else
              CurrLine = LinkedListInsert(CurrLine, " movf " + V2 + ",W")
            End If
            CurrLine = LinkedListInsert(CurrLine, " subwf " + V1 + ",W")
          End If
          IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN
            Cmd = " btfss STATUS, Z"
            IF OP = "=" THEN Cmd = " btfsc STATUS, Z"
          END IF
          IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN
            Cmd = " btfsc STATUS, Z"
            IF OP = "=" THEN Cmd = " btfss STATUS, Z"
          END IF
          CurrLine = LinkedListInsert(CurrLine, Cmd)
        ElseIf ModeAVR Then

          'Compare is inserted above

          'Branch
          IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN
            Cmd = " breq PC + 2"
            IF OP = "=" THEN Cmd = " brne PC + 2"
          END IF
          IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN
            Cmd = " brne PC + 2"
            IF OP = "=" THEN Cmd = " breq PC + 2"
          END IF
          CurrLine = LinkedListInsert(CurrLine, Cmd)

        END IF
      END IF

      'Code for greater than/less than
      IF OP = "<" OR OP = ">" THEN
        'COSC = COSC + 1: CheckTemp(COSC) = " bsf STATUS, C"
        If ModePIC Then
          IF OP = ">" THEN Swap V1, V2
          'Take bigger from smaller. V2 should be bigger. Take V2 from V1
          'mov small into W
          'Sub big from W
          IF IsConst(V1) And ChipFamily = 12 THEN
            If InStr(V1, "@") <> 0 Then
              CurrLine = LinkedListInsert(CurrLine, " movlw " + V1)
            Else
              CurrLine = LinkedListInsert(CurrLine, " movlw " + Str(MakeDec(V1)))
            End If
            CurrLine = LinkedListInsert(CurrLine, " movwf SysIFTemp")
            V1 = "SysIFTemp": AddVar V1, "BYTE", 1, 0, "REAL", Origin, , -1
          END If

          IF IsConst(V2) THEN Cmd = " movlw " + Str(MakeDec(V2))
          IF NOT IsConst(V2) THEN Cmd = " movf " + V2 + ",W"
          CurrLine = LinkedListInsert(CurrLine, Cmd)

          If IsConst(V1) Then
            CurrLine = LinkedListInsert(CurrLine, " sublw " + Str(MakeDec(V1)))
          Else
            CurrLine = LinkedListInsert(CurrLine, " subwf " + V1 + ",W")
          End If

          IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN Cmd = " btfss STATUS, C"
          IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN Cmd = " btfsc STATUS, C"
          CurrLine = LinkedListInsert(CurrLine, Cmd)

        ElseIf ModeAVR Then
          IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN CurrLine = LinkedListInsert(CurrLine, " brsh PC + 2")
          IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN CurrLine = LinkedListInsert(CurrLine, " brlo PC + 2")

        End If
      END IF

      'Code for less or equal/more or equal
      IF OP = "{" OR OP = "}" THEN
        If ModePIC Then
          IF OP = "{" THEN Swap V1, V2
          'Take smaller from bigger. V1 should be bigger or equal. Take V2 from V1
          'mov small into W
          'Sub big from W

          IF IsConst(V1) And ChipFamily = 12 THEN
            If InStr(V1, "@") <> 0 Then
              CurrLine = LinkedListInsert(CurrLine, " movlw " + V1)
            Else
              CurrLine = LinkedListInsert(CurrLine, " movlw " + Str(MakeDec(V1)))
            End If
            CurrLine = LinkedListInsert(CurrLine, " movwf SysIFTemp")
            V1 = "SysIFTemp": AddVar V1, "BYTE", 1, 0, "REAL", Origin, , -1
          END If

          IF IsConst(V2) Then
            If InStr(V2, "@") <> 0 Then
              Cmd = " movlw " + V2
            Else
              Cmd = " movlw " + Str(MakeDec(V2))
            End If
          End If
          IF NOT IsConst(V2) THEN Cmd = " movf " + V2 + ",W"
          CurrLine = LinkedListInsert(CurrLine, Cmd)

          'CurrLine = LinkedListInsert(CurrLine, " subwf " + V1 + ",W")
          If IsConst(V1) Then
            CurrLine = LinkedListInsert(CurrLine, " sublw " + Str(MakeDec(V1)))
          Else
            CurrLine = LinkedListInsert(CurrLine, " subwf " + V1 + ",W")
          End If

          IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN Cmd = " btfsc STATUS, C"
          IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN Cmd = " btfss STATUS, C"
          CurrLine = LinkedListInsert(CurrLine, Cmd)

        ElseIf ModeAVR Then
          IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN CurrLine = LinkedListInsert(CurrLine, " brlo PC + 2")
          IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN CurrLine = LinkedListInsert(CurrLine, " brsh PC + 2")
        End If
      END IF

    END IF

  End IF

  'More than one condition or not byte, so compile as calculation
 ComplexCondition:
  IF Complex = 1 THEN

    DO WHILE INSTR(Condition, " ") <> 0: Replace Condition, " ", "": LOOP
    T = 0
    Sum = Condition


    If HashMapGet(Constants, "DISABLE1173" ) = 0 then
      If ModeAVR  Then
        'resolve System bit tests silent failures where complex bit tests with =1 or -=0 fail
        Dim as Integer SysByteAddress, SysBitAddress, SysByteCounter,SysBitCounter, SysFound, Conditions
        Dim SysTempSearchString as String
        SysFound = 0
        Conditions = 0
        For SysByteCounter = 0 to 8 'max of 8 assumed... could be more. I have no way of knowing as this point of the process
          For SysBitCounter = 0 to 7
            SysTempSearchString = "SYSBITVAR"+Str( SysByteCounter )+"."+Str(SysBitCounter)
            If Instr( Sum, SysTempSearchString ) <> 0  Then
                SysFound = SysFound + 1
            End if
          Next
        Next
        'Find some chars that offend
        Conditions = CountOccur(Sum, "<")
        Conditions = Conditions + CountOccur(Sum, ">")
        Conditions = Conditions + CountOccur(Sum, "=")
        Conditions = Conditions + CountOccur(Sum, "{")
        Conditions = Conditions + CountOccur(Sum, "}")
        Conditions = Conditions + CountOccur(Sum, "~")


        If SysFound > 1 and Conditions > 0 Then LogError "Potential invalid BIT comparison error when using complex Bitwise logic.  Reduce complexity of Bitwise comparison", Origin
      End if
    End If

    FreeCalcVar ""

    Dim As CodeSection Pointer CodeDest
    CodeDest = 0

    CompileCalc(Sum, "", Origin, CodeDest)
    NewCode = CodeDest->CodeList
    If NewCode->Next <> 0 Then
      CurrLine = LinkedListInsertList(CurrLine, NewCode)
      If ModePIC Then
        IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN Cmd = " btfsc " + Sum + ",0"
        IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN Cmd = " btfss " + Sum + ",0"
      ElseIf ModeAVR Then
        IF INSTR(UCase(IfTrue), "TRUE") <> 0 THEN Cmd = " sbrc " + Sum + ",0"
        IF INSTR(UCase(IfTrue), "FALSE") <> 0 THEN Cmd = " sbrs " + Sum + ",0"
      End If
      CurrLine = LinkedListInsert(CurrLine, Cmd)
    Else
      If MakeDec(Sum) = 0 Then
        Condition = "AlwaysFalse"
      Else
        Condition = "AlwaysTrue"
      End If
      Return OutList
    End If
  END IF

  Return OutList
 End Function

Sub CompileDim (CurrSub As SubType Pointer)
  Dim As String VarName, VarType, VarAlias, VarFixedLocIn, Origin, InLine, SiStr, Temp
  Dim As String AliasList(16)
  Dim As Integer PD, IsAlias, Si, CD, VarFixedLoc, NewVarCount, ALC
  Dim As LinkedListElement Pointer CurrLine

  'Don't try to read dims from macro
  'Only read from macro invocation
  If CurrSub->IsMacro Then Return

  Dim NewVarList(100) As String
  NewVarCount = 0

  'Mark Sub as having had variables read
  'Haven't actually read them yet (won't until end of this sub)
  'But might cause an infinite loop if this isn't set
  CurrSub->VarsRead = -1

  CurrLine = CurrSub->CodeStart->Next
  Do While CurrLine <> 0
    InLine = CurrLine->Value

    If Left(InLine, 4) = "DIM " THEN

      InLine = Mid(InLine, 5)
      CurrLine = LinkedListDelete(CurrLine)

      'Get origin
      Origin = ""
      If INSTR(InLine, ";?F") <> 0 THEN
        Origin = Mid(InLine, INSTR(InLine, ";?F"))
        InLine = RTrim(Left(InLine, INSTR(InLine, ";?F") - 1))
      END IF

      'Get name
      VarName = InLine
      If INSTR(VarName, " AS ") <> 0 THEN VarName = Left(VarName, INSTR(VarName, " AS ") - 1)
      If INSTR(VarName, " ALIAS ") <> 0 THEN VarName = Left(VarName, INSTR(VarName, " ALIAS ") - 1)
      If INSTR(VarName, " AT ") <> 0 THEN VarName = Left(VarName, INSTR(VarName, " AT ") - 1)
      VarName = Trim(VarName)
      If INSTR(VarName, ",") = 0 Then
        NewVarList(1) = VarName
        NewVarCount = 1
      Else
        NewVarCount = 0
        Do While INSTR(VarName, ",") <> 0
          NewVarCount += 1
          NewVarList(NewVarCount) = Trim(Left(VarName, INSTR(VarName, ",") - 1))
          VarName = Trim(Mid(VarName, INSTR(VarName, ",") + 1))
        Loop
        NewVarCount += 1
        NewVarList(NewVarCount) = VarName
      End If

      'Get type
      VarType = "BYTE"
      IF INSTR(InLine, " AS ") <> 0 Then
        VarType = Mid(InLine, INSTR(InLine, " AS ") + 3)
        IF INSTR(VarType, "(") <> 0 Then VarType = Left(VarType, INSTR(VarType, "(") - 1)
        IF INSTR(VarType, " ALIAS ") <> 0 Then VarType = Left(VarType, INSTR(VarType, " ALIAS ") - 1)
        IF INSTR(VarType, " AT ") <> 0 Then VarType = Left(VarType, INSTR(VarType, " AT ") - 1)
        VarType = Trim(UCase(VarType))
      END IF

      'Check if alias
      IsAlias = 0
      VarAlias = ""
      IF INSTR(InLine, " ALIAS ") <> 0 Then
        IsAlias = -1
        VarAlias = Trim(Mid(InLine, INSTR(InLine, " ALIAS ") + 7))
        IF INSTR(VarAlias, " AS ") <> 0 Then VarAlias = Left(VarAlias, INSTR(VarAlias, " AS ") - 1)
        IF INSTR(VarAlias, " AT ") <> 0 Then VarAlias = Left(VarAlias, INSTR(VarAlias, " AT ") - 1)
      End If

      'Check if fixed location specified
      VarFixedLoc = -1
      If InStr(InLine, " AT ") <> 0 Then
        VarFixedLocIn = Trim(Mid(InLine, INSTR(InLine, " AT ") + 4))
        IF INSTR(VarFixedLocIn, " AS ") <> 0 Then VarFixedLocIn = Left(VarFixedLocIn, INSTR(VarFixedLocIn, " AS ") - 1)
        IF INSTR(VarFixedLocIn, " ALIAS ") <> 0 Then VarFixedLocIn = Left(VarFixedLocIn, INSTR(VarFixedLocIn, " ALIAS ") - 1)

        'Is a calculation needed to get location?
        If IsCalc(VarFixedLocIn) Then
          Calculate VarFixedLocIn
        End If

        'Is fixed location given as a constant?.
        If IsConst(VarFixedLocIn) Then
          VarFixedLoc = MakeDec(VarFixedLocIn)
        'Is fixed location given as a reference to an SFR?
        Else
          'Not supported, but can be added later if needed
        End If
        ' Print "Var loc: "; Varname, VarFixedLoc

        'If also an alias, show an error. Can't be in two locations at once
        If IsAlias Then
          LogError(Message("VarAliasAndAt"), Origin)
        End If
      End If

      'Ensure alias variable is also declared
      If IsAlias Then
        GetTokens (VarAlias, AliasList(), ALC, ",")
        For PD = 1 To ALC
          If Not IsConst(AliasList(PD)) And Not IsCalc(AliasList(PD)) Then
            'Is variable an alias to itself?
            Temp = Right(AliasList(PD), 2)
            If Temp = "_H" Or Temp = "_U" Or Temp = "_E" Then
              Temp = Left(AliasList(PD), Len(AliasList(PD)) - 2)
            Else
              Temp = AliasList(PD)
            End If
            If Temp = VarName Then
              'Trying to add self as alias, prevent
              LogError(Message("AliasToSelf"), Origin)
              VarAlias = ""
              IsAlias = 0
            End If
          End If
        Next
      End If

      For CV = 1 to NewVarCount
        InLine = NewVarList(CV)
        'Get size
        Si = 1

        'added error handling for arrays and processing of arrays using constant
        IF INSTR(InLine, "(") <> 0 Then
          If CountOccur( InLine, "(" ) <> CountOccur( InLine, ")" ) Then
            SiStr = Message("BadVarName")
            Replace SiStr, "%var%", InLine
            LogError SiStr, Origin
            goto process_next_line:
          End If
          SiStr = Mid(InLine, INSTR(InLine, "(") )

          If IsCalc(SiStr) Then
            Calculate SiStr
            Si = VAL(SiStr)
          Else
            Si = VAL(Mid(InLine, INSTR(InLine, "(") + 1))
          End If

          InLine = Trim(Left(InLine, INSTR(InLine, "(") - 1))
        End If

        'Check variable name
        Dim ValidVarName As Integer = -1
        If VarType = "BIT" AndAlso Left(InLine, 9) = "SYSBITVAR" AndAlso InStr(InLine, ".") <> 0 Then
          'Redefining bit var, ignore
          ValidVarName = 0

        ElseIf IsConst(InLine) Or Not IsValidName(InLine) Then
          Temp = Message("BadVarName")
          If Left(InLine, 7) = ";STRING" Then
            Replace Temp, "%var%", Chr(34) + GetString(InLine, 0) + Chr(34)
          Else
            Replace Temp, "%var%", InLine
          End If
          If InStr(Origin, ";STARTUP") <> 0 Then
            Origin = RTrim(Left(Origin, InStr(Origin, ";STARTUP") - 1))
          EndIf
          LogError Temp, Origin
          ValidVarName = 0
        END If

        'Add var
        If ValidVarName Then
          If IsAlias Then
            AddVar(InLine, VarType, Si, CurrSub, "ALIAS:" + VarAlias, Origin, , -1, 0)
          Else
            ' print InLine, VarType, Si, CurrSub, "REAL", Origin, VarFixedLoc, -1, 0;
            AddVar(InLine, VarType, Si, CurrSub, "REAL", Origin, VarFixedLoc, -1, 0)
          End If
        End If
      Next
    End If
    process_next_line:
    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub CompileDir (CompSub As SubType Pointer)

  Dim As String Origin, InLine, VarName, Port, Temp, Direction
  Dim As String TrisPort, PortLetter
  Dim As Integer DirPort, NotIOPort, SearchPos, FoundPos
  Dim As LinkedListElement Pointer CurrLine
  Dim As PinDirType Pointer CurrPinDir
  Dim As SysVarType Pointer CurrVar

  FoundCount = 0

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    InLine = CurrLine->Value

    IF Left(InLine, 4) = "DIR " Then
      Origin = ""
      IF INSTR(InLine, ";?F") <> 0 THEN
        Origin = Mid(InLine, INSTR(InLine, ";?F"))
        InLine = RTrim(Left(InLine, INSTR(InLine, ";?F") - 1))
      END IF

      FoundCount += 1
      InLine = Trim(Mid(InLine, 4))
      Direction = LTrim(Mid(InLine, INSTR(InLine, " ") + 1))
      Port = UCase(RTrim(Left(InLine, INSTR(InLine, " ") - 1)))

      'Handle original (early 2006) form of Dir command (ie. Dir B0 Output)
      IF INSTR(Port, "PORT") = 0 AND (Left(ChipName, 2) = "16" Or Left(ChipName, 2) = "18") AND LEN(Port) = 2 THEN
        Port = "PORT" + Left(Port, 1) + "." + Mid(Port, 2)
      'early 2006 version of full port (ie. Dir B number)
      ElseIf Len(Port) = 1 AND (Left(ChipName, 2) = "16" Or Left(ChipName, 2) = "18") Then
        Port = "PORT" + Port
      End If
      If Left(ChipName, 2) = "10" Or Left(ChipName, 2) = "12" Then
        If Port = "" Or Port = "IO" Then
          Port = "GPIO"
        End If
      End If
      Replace Direction, "PUT", ""

      'Should now have PORTx, GPIO, PORTx.y or GPIO.y in Port. If not, something is wrong
      'Check for sys var bit that refers to port bit
      CurrVar = HashMapGet(SysVarBits, Port)
      If CurrVar <> 0 Then
        Port = CurrVar->Parent + "." + Str(CurrVar->Location)
      End If

      'Determine if DIR sets 1 bit or whole port
      DirPort = -1
      IF INSTR(Port, ".") <> 0 THEN DirPort = 0

      'Get the variable that sets port direction
      PortLetter = Port
      Replace PortLetter, "PORT", ""
      Replace PortLetter, "GP", ""
      Replace PortLetter, "LAT", ""
      Replace PortLetter, "PIN", ""
      TrisPort = PortLetter
      If InStr(TrisPort, ".") <> 0 Then TrisPort = Left(TrisPort, InStr(TrisPort, ".") - 1)
      If ModePIC Then
        VarName = "TRIS" + PortLetter
      ElseIf ModeAVR Then
        VarName = "DDR" + PortLetter
      End If

      'Check that a pin is being set
      'TrisPort should be a single letter or IO, no bits
      NotIOPort = 0

      IF LEN(TrisPort) <> 1 AND TrisPort <> "IO" THEN 
        NotIOPort = -1
      '  print 1
      END IF
      If TrisPort < "A" Or TrisPort > "Z" Then 
        NotIOPort = -1
       ' print 2
      END IF
      'SFR for variable should exist, unless 12 bit core PIC
      Temp = VarName
      If Instr(Temp, ".") <> 0 Then 
        Temp = Left(Temp, Instr(Temp, ".") - 1)
        'print 3
      END IF
      If ChipFamily <> 12 And GetSysVar(Temp) = 0 Then 
        NotIOPort = -1
        'print 4
      END IF
      'Error if port is not valid
      If NotIOPort Then
        Temp = Message("NotIO")
        Replace Temp, "%var%", Port
        LogError Temp, Origin
      End If

      'Add code
      'Whole port mode
      IF DirPort THEN

        'Record settings
        CurrPinDir = GetPinDirection(Port)
        If CurrPinDir <> 0 Then
          If Direction = "IN" Then
            CurrPinDir->SetIn = -1
            CurrPinDir->WholePort = -1
            CurrPinDir->WholePortDir = 255
          ElseIf Direction = "OUT" Then
            CurrPinDir->SetOut = -1
            CurrPinDir->WholePort = -1
            CurrPinDir->WholePortDir = 0
          Else
            'If port is set to mixed direction, store direction
            If IsConst(Direction) Then
              CurrPinDir->WholePort = -1
              CurrPinDir->WholePortDir = MakeDec(Direction)
            Else
              CurrPinDir->WholePort = -1
              CurrPinDir->WholePortDir = -1
            End If
          End If
        End If

        'Generate code
        IF Direction = "IN" THEN
          If ModePIC Then
            If ChipFamily = 12 And Not IsIOReg(VarName) Then
              CurrLine->Value = " movlw 255"
              CurrLine = LinkedListInsert(CurrLine, " tris " + Port)
            Else
              If PSP = 1 AND VarName = "TRISE" Then
                CurrLine->Value = VarName + "=239"
              Else
                CurrLine->Value = VarName + "=255"
              End If
            End If
          ElseIf ModeAVR Then
            CurrLine = LinkedListDelete(CurrLine)
            CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("0", VarName, Origin))
          End If

        ElseIf Direction = "OUT" THEN
          If ModePIC Then
            If ChipFamily = 12 And Not IsIOReg(VarName) Then
              CurrLine->Value = " clrw"
              CurrLine = LinkedListInsert(CurrLine, " tris " + Port)
            Else
              CurrLine->Value = " clrf " + VarName
            End If
          ElseIf ModeAVR Then
            CurrLine = LinkedListDelete(CurrLine)
            CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("255", VarName, Origin))
          End If

        Else
          If ChipFamily = 12 And Not IsIOReg(VarName) Then
            If IsConst(Direction) Then
              CurrLine->Value = " movlw " + Direction
              CurrLine = LinkedListInsert(CurrLine, " tris " + Port)
            Else
              CurrLine->Value = " movf " + Direction + ",W"
              CurrLine = LinkedListInsert(CurrLine, " tris " + Port)
            End If
          Else
            CurrLine->Value = VarName + "=" + Direction
          End If
        End If

      'Single pin mode
      Else
        'On 12 bit PIC, need to create shadow register
        'Can't set individual pin directions otherwise
        If ChipFamily = 12 Then
          FoundPos = -1
          For SearchPos = 1 To PinDirShadows
            If PinDirShadow(SearchPos) = TrisPort Then
              FoundPos = SearchPos
              Exit For
            End If
          Next
          If FoundPos = -1 Then
            PinDirShadows += 1
            PinDirShadow(PinDirShadows) = TrisPort
          End If
        End If

        'Record setting
        CurrPinDir = GetPinDirection(Port)
        If CurrPinDir <> 0 Then
          If Direction = "IN" Then
            CurrPinDir->SetIn = -1

            'Check that pin can be input
            If CurrPinDir->AllowedDirections <> "" And InStr(CurrPinDir->AllowedDirections, "i") = 0 Then
              Temp = Message("WarningPinNotInput")
              Replace Temp, "%pin%", Port
              LogWarning Temp, Origin
            End If

          ElseIf Direction = "OUT" Then
            CurrPinDir->SetOut = -1

            'Check that pin can be output
            If CurrPinDir->AllowedDirections <> "" And InStr(CurrPinDir->AllowedDirections, "o") = 0 Then
              Temp = Message("WarningPinNotOutput")
              Replace Temp, "%pin%", Port
              LogWarning Temp, Origin
            End If

          End If
        End If

        'Generate code
        If ModePIC Then
          'Generate error when the output port is missing.  PORTC.   No bit address
          If Right(Trim(Varname),1) = "." Then
            Temp = Message("NotIO")
            Replace Temp, "%var%", InLine
            LogError temp , Origin
          End If 
          Replace VarName, ".", ","
          If Direction = "IN" Then
            CurrLine->Value = " bsf " + VarName
          ElseIf Direction = "OUT" Then
            CurrLine->Value = " bcf " + VarName
          Else
            LogError Message("BadDirection"), Origin
          End If
        ElseIf ModeAVR Then
          CurrLine = LinkedListDelete(CurrLine)
          If Direction = "IN" Then
            CurrLine = LinkedListInsertList(CurrLine, GenerateBitSet(VarName, "0", Origin))
          ElseIf Direction = "OUT" Then
            CurrLine = LinkedListInsertList(CurrLine, GenerateBitSet(VarName, "1", Origin))
          Else
            LogError Message("BadDirection"), Origin
          End If
        End If
      End If

    End If
    CurrLine = CurrLine->Next
  Loop
 END SUB

Sub CompileDo (CompSub As SubType Pointer)
  FoundCount = 0
  Dim As String InLine, Temp, Origin, Mode, Condition, LoopOrigin
  Dim As Integer DL, CP, ExitFound, WD
  Dim As LinkedListElement Pointer CurrLine, FindLoop, LoopLoc, NewCode, OldLoopLoc

  DL = 0
  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    InLine = UCase(CurrLine->Value)
    If Left(InLine, 3) = "DO " OR InLine = "DO" THEN

      'Get origin
      Origin = ""
      IF INSTR(InLine, ";?F") <> 0 THEN
        Origin = Mid(InLine, INSTR(InLine, ";?F"))
        InLine = RTrim(Left(InLine, INSTR(InLine, ";?F") - 1))
      END IF

      'Get position of condition
      CP = 0
      IF Left(InLine, 3) = "DO " THEN CP = 1
      DL = 1 'Nesting level
      ExitFound = 0 'Exit Do found?
      DLC = DLC + 1 'Increment do loop count
      CurrLine->Value = "SysDoLoop_S" + Str(DLC) + LabelEnd 'Add label to mark start
      GetMetaData(Currline)->IsLabel = -1

      'Find exit do, loop
      LoopLoc = 0
      FindLoop = CurrLine->Next
      Do While FindLoop <> 0
        Temp = UCase(FindLoop->Value)
        IF Left(Temp, 5) = "LOOP " OR Temp = "LOOP" THEN DL = DL - 1
        IF Left(Temp, 3) = "DO " OR Temp = "DO" THEN DL = DL + 1
        IF Left(Temp, 7) = "EXIT DO" AND DL = 1 Then
          ExitFound = 1
          If ModePIC Then
            FindLoop->Value = " goto SysDoLoop_E" + Str(DLC)
          End if
          If ModeAVR Then FindLoop->Value = " rjmp SysDoLoop_E" + Str(DLC)
          If ModeZ8 Then FindLoop->Value = " jp SysDoLoop_E" + Str(DLC)
        End If
        IF DL = 0 THEN LoopLoc = FindLoop: Exit Do
        FindLoop = FindLoop->Next
      Loop

      'Show error if no loop
      If LoopLoc = 0 Then
        LogError(Message("DoWithoutLoop"), Origin)

      'Otherwise, compile normally
      Else
        'Get origin of LOOP command
        LoopOrigin = ""
        IF INSTR(LoopLoc->Value, ";?F") <> 0 THEN
          LoopOrigin = Mid(LoopLoc->Value, INSTR(LoopLoc->Value, ";?F"))
          LoopLoc->Value = RTrim(Left(LoopLoc->Value, INSTR(LoopLoc->Value, ";?F") - 1))
        END If
        If LoopOrigin = "" Then LoopOrigin = Origin

        'Is the condition after the LOOP?
        IF Left(LoopLoc->Value, 5) = "LOOP " THEN CP = 2

        'Get mode {UNTIL | WHILE}, condition
        IF CP = 1 THEN
          Mode = LTrim(Mid(InLine, 4))
          If InStr(Mode, " ") <> 0 Then
            Condition = LCase(Mid(Mode, INSTR(Mode, " ") + 1))
            Mode = UCase(Left(Mode, INSTR(Mode, " ") - 1))
          End If

          'Is mode valid?
          If Mode <> "UNTIL" And Mode <> "WHILE" Then
            Temp = Message("InvalidDoMode")
            Replace Temp, "%mode%", Mode
            LogError Temp, Origin
          Else
            'Is condition present?
            If Condition = "" Then
              Temp = Message("DoWithoutCondition")
              Replace Temp, "%mode%", Mode
              LogError Temp, Origin
            End If
          End If
        ElseIf CP = 2 THEN
          Mode = LTrim(Mid(LoopLoc->Value, 6))
          If InStr(Mode, " ") <> 0 Then
            Condition = LCase(Mid(Mode, INSTR(Mode, " ") + 1))
            Mode = UCase(Left(Mode, INSTR(Mode, " ") - 1))
          End If

          If Mode <> "UNTIL" And Mode <> "WHILE" Then
            Temp = Message("InvalidDoMode")
            Replace Temp, "%mode%", Mode
            LogError Temp, LoopOrigin
          Else
            'Is condition present?
            If Condition = "" Then
              Temp = Message("DoWithoutCondition")
              Replace Temp, "%mode%", Mode
              LogError Temp, LoopOrigin
            End If
          End If
        END IF

        OldLoopLoc = LoopLoc

        'Compile with no condition
        If CP = 0 Then
          If ModePIC Then Temp = " goto SysDoLoop_S" + Str(DLC)
          If ModeAVR Then Temp = " rjmp SysDoLoop_S" + Str(DLC)
          If ModeZ8 Then Temp = " jp SysDoLoop_S" + Str(DLC)
          LoopLoc = LinkedListInsert(LoopLoc, Temp)

        'Compile with condition after DO
        ElseIf CP = 1 THEN
          Temp = "TRUE": IF Mode = "WHILE" THEN Temp = "FALSE"
          NewCode = CompileConditions(Condition, Temp, Origin)
          If (Mode = "WHILE" And Condition <> "AlwaysTrue") Or (Mode = "UNTIL" And Condition <> "AlwaysFalse") Then
            CurrLine = LinkedListInsertList(CurrLine, NewCode)
            If ModePIC Then CurrLine = LinkedListInsert(CurrLine, " goto SysDoLoop_E" + Str(DLC))
            If ModeAVR Then CurrLine = LinkedListInsert(CurrLine, " rjmp SysDoLoop_E" + Str(DLC))
            If ModeZ8 Then CurrLine = LinkedListInsert(CurrLine, " jp SysDoLoop_E" + Str(DLC))
          End If

          If ModePIC Then Temp = " goto SysDoLoop_S" + Str(DLC)
          If ModeAVR Then Temp = " rjmp SysDoLoop_S" + Str(DLC)
          If ModeZ8 Then Temp = " jp SysDoLoop_S" + Str(DLC)
          LoopLoc = LinkedListInsert(LoopLoc, Temp)

        'Compile with condition after LOOP
        ElseIf CP = 2 THEN
          Temp = "TRUE": IF Mode = "UNTIL" THEN Temp = "FALSE"
          NewCode = CompileConditions(Condition, Temp, LoopOrigin)
          If (Mode = "WHILE" And Condition <> "AlwaysFalse") Or (Mode = "UNTIL" And Condition <> "AlwaysTrue") Then
            LoopLoc = LinkedListInsertList(LoopLoc, NewCode)

            If ModePIC Then Temp = " goto SysDoLoop_S" + Str(DLC)
            If ModeAVR Then Temp = " rjmp SysDoLoop_S" + Str(DLC)
            If ModeZ8 Then Temp = " jp SysDoLoop_S" + Str(DLC)
            LoopLoc = LinkedListInsert(LoopLoc, Temp)
          END IF
        End If

        'Create label for end of loop
        LoopLoc = LinkedListInsert(LoopLoc, "SysDoLoop_E" + Str(DLC) + LabelEnd)
        GetMetaData(LoopLoc)->IsLabel = -1
        'Delete Loop statement
        LinkedListDelete(OldLoopLoc)
      End IF
    End If

    CurrLine = CurrLine->Next
  Loop
  FoundCount = DLC
  
  End Sub

Sub CompileExitSub (CompSub As SubType Pointer)

  Dim As String Temp
  Dim As LinkedListElement Pointer CurrLine
  FoundCount = 0

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    IF Left(UCase(CurrLine->Value), 8) = "EXIT SUB" OR Left(UCase(CurrLine->Value), 13) = "EXIT FUNCTION" THEN
      If ModePIC Then CurrLine->Value = " return"
      If ModeAVR Then CurrLine->Value = " ret"
      If ModeZ8 Then CurrLine->Value = " ret"
      FoundCount = FoundCount + 1
    End If

    IF Left(UCase(CurrLine->Value), 4) = "END " THEN
      Temp = CurrLine->Value
      IF INSTR(Temp, ";") <> 0 THEN Temp = Trim(Left(Temp, INSTR(Temp, ";") - 1))
      IF Temp = "END" THEN
        If ModePIC Then
          CurrLine->Value = " goto BASPROGRAMEND"
        End If
        If ModeAVR Then CurrLine->Value = " rjmp BASPROGRAMEND"
        If ModeZ8 Then CurrLine->Value = " jp BASPROGRAMEND"
        FoundCount = FoundCount + 1
      END IF
    END IF
    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub CompileFor (CompSub As SubType Pointer)

  Dim As String InLine, Origin, Temp, ErrorOrigin
  Dim As String LoopVar, StartValue, EndValue, StepValue, LoopVarType
  Dim As Integer FL, CD, StepIntVar, StepExists
  Dim As LinkedListElement Pointer CurrLine, FindLoop, LoopLoc
  Dim As Integer IgnoreWarning, IntegerEndVariableCreated, EndValueConstantInitialValue

  'force use at RC43 + RC44
  If HashMapGet(Constants, "USELEGACYFORNEXT" ) = 0 then
    ADDCONSTANT("NEWNEXTFORHANDLER","")
  end if

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0

    InLine = CurrLine->Value

    IF Left(InLine, 4) = "FOR " THEN

      FL = 0
      StepExists = 0
      IgnoreWarning = 0
      IntegerEndVariableCreated = 0
      Origin = ""
      IF INSTR(InLine, ";?F") <> 0 THEN
        Origin = Mid(InLine, INSTR(InLine, ";?F"))
        InLine = RTrim(Left(InLine, INSTR(InLine, ";?F") - 1))
      END IF

      Replace InLine, " TO ", Chr(30)

      IF INSTR(InLine, " STEP ") <> 0 THEN
        Replace InLine, " STEP ", Chr(31)
      END IF

      IF INSTR(InLine, "#IGNOREWARNING") <> 0 THEN
        Replace InLine, "#IGNOREWARNING", ""
        IgnoreWarning = -1
      END IF

      'enable new handler  NewNextForhandler force though new handler!
      If HashMapGet(Constants, "NEWNEXTFORHANDLER" ) then
        StepExists = -1
      End if

      DO WHILE INSTR(InLine, " ") <> 0: Replace InLine, " ", "": LOOP
      LoopVar = Mid(InLine, 4)
      LoopVar = Left(LoopVar, INSTR(LoopVar, "=") - 1)
      StartValue = Mid(InLine, INSTR(InLine, "=") + 1)
      StartValue = Left(StartValue, INSTR(StartValue, Chr(30)) - 1)
      EndValue = Mid(InLine, INSTR(InLine, Chr(30)) + 1)
      StepValue = "1"
      StepIntVar = 0
      IF INSTR(InLine, Chr(31)) <> 0 THEN
        EndValue = Left(EndValue, INSTR(EndValue, Chr(31)) - 1)
        StepValue = Mid(InLine, INSTR(InLine, Chr(31)) + 1)
        If Not IsConst(StepValue) And TypeOfVar(StepValue, CompSub) = "INTEGER" Then
          StepIntVar = -1
          'Print "Found Integer step variable:" + StepValue
        EndIf

        If HashMapGet(Constants, "NEWNEXTFORHANDLER" )  then
          'create an integer from the constant, only not ABS(1)
          If IsConst(StepValue) and ABS(Val(StepValue)) <> 1 then
             AddVar "SysForLoopStep" + Str(FLC), "INTEGER", 1, 0, "REAL", "", , -1

             CurrLine = LinkedListInsert(CurrLine,  "SysForLoopStep" + Str(FLC) + " = " + StepValue)
             CurrLine = CurrLine->Prev
             CurrLine = LinkedListInsert(CurrLine,  ";Init SysForLoopStep" + Str(FLC)+" :#0" )
             CurrLine = CurrLine->Prev
             'replace the constant with the integer string name
             StepValue = "SysForLoopStep" + Str(FLC)
             StepIntVar = -1
          End if
        End if

      END IF
  'Print "For variable:"; LoopVar; " start:"; StartValue; " end:"; EndValue; " step:"; StepValue

      EndValueConstantInitialValue = val(EndValue)

      'Check that variable can hold start and end values
      LoopVarType = TypeOfValue(LoopVar, CompSub)
      If CastOrder(TypeOfValue(StartValue, CompSub)) = -1 Then
        LogError(Message("ForBadStart2"), Origin)
        Exit Sub
      End If
      If CastOrder(LoopVarType) < CastOrder(TypeOfValue(StartValue, CompSub))  Then
        LogError(Message("ForBadStart"), Origin)
      End If
      If CastOrder(LoopVarType) < CastOrder(TypeOfValue(EndValue, CompSub)) Then
        LogError(Message("ForBadEnd"), Origin)
      End If

      If HashMapGet(Constants, "NEWNEXTFORHANDLER" ) = 0 then
      'this is bonkers when you want to use the overflow!  255 to 10 for instant
          'Negate step value if necessary -
          IF VAL(EndValue) < VAL(StartValue) AND IsConst(EndValue) AND IsConst(StartValue) AND (InStr(StepValue, "-") = 0 And Not StepIntVar) THEN
            CurrLine = LinkedListInsert(CurrLine, ";For-next step value was automaticaly negated by compiler")
            CurrLine = CurrLine->Prev
            StepValue = "-" + StepValue
          End if
      End if

      'Use step value 1 as default when variables are used to force through new code - NOT NEEDED....
      IF ( ( NOT IsConst(EndValue) OR  NOT IsConst(StartValue) )  And Not StepIntVar) THEN
        If HashMapGet(Constants, "NEWNEXTFORHANDLER" ) then
           StepExists = -1
        End if
      End if

      If HashMapGet(Constants, "NEWNEXTFORHANDLER" ) THEN
        If Not IsConst(StepValue) And TypeOfVar(StepValue, CompSub) <> "INTEGER" THEN
      'Prevent using a negate with a variable... we cannot handle
            If Left(StepValue,1) <> "-" then
              LogError(Message("ForBadStepVariable"), Origin)
            Else
              LogError(Message("ForBadStepNegate"), Origin)
            End if
        End if
      End if


      FLC = FLC + 1
      FL = 1

      'And, finally determine if we can use the old method for specific use cases of constants and step=1, belt and braces check
      IF ( VAL(EndValue) > VAL(StartValue) ) AND IsConst(EndValue) AND IsConst(StartValue) AND (InStr(StepValue, "-") = 0 And Val(StepValue)=1 ) THEN
          StepExists  = 0
      End if

      ' A final, final check to try and force legacy....
      If StepExists = -1 Then
        If ABS(Val(Stepvalue)) = 1 Then 
          If  ( TypeOfValue(LoopVar, Subroutine(GetSubID(Origin)))= "BYTE" or _ 
                TypeOfValue(LoopVar, Subroutine(GetSubID(Origin)))= "WORD" or _ 
                TypeOfValue(LoopVar, Subroutine(GetSubID(Origin)))= "LONG" or _ 
                TypeOfValue(LoopVar, Subroutine(GetSubID(Origin)))= "INTEGER" ) Then
            StepExists = 0
          End If
        End If
      End If

      If StepExists Then

          'New Starting code. This is common code to the negative or positive StepValue value
          CurrLine->Value = LoopVar + " =" + StartValue +  Origin + "[ao]"  'need to add [ao] to ensure the loop value type is correct
          'Create a Loopx (see Pseudo code)
          CurrLine = LinkedListInsert(CurrLine, "SysForLoop" + Str(FLC) + LabelEnd)
          GetMetaData(Currline)->IsLabel = -1

      Else
          'Old code - still used for NO STEP
          'old Pseudo code:

          'V = SV - ST
          'SysForLoop(n):
          'V += ST
          '...
          'if V < EV then goto SysForLoop(n)

          'Changed to this: (27/6/2010)
          'V = SV
          'SysForLoop(n):
          'if V > EV then goto SysForLoopEnd(n)
          '...
          'V += ST
          'goto SysForLoop(n)
          '(New version more in line with For in other dialects)

          'Then to this: (27/8/2010)
          'V = SV - ST
          'If SV not const or EV not const, if V > EV then goto SysForLoopEnd(n)
          'SysForLoop(n):
          'V += ST
          '...
          'if V <= EV then goto SysForLoop(n)
          'SysForLoopEnd(n):


          'Starting code
          If PreserveMode <> 0 then
            CurrLine = LinkedListInsert(CurrLine->Prev, ";Legacy method")
            CurrLine = CurrLine->Next
          End If
          CurrLine->Value = LoopVar + "=" + StartValue + "-" + StepValue + Origin + "[ao]"

          'If StartValue or EndValue are variables, need to check values at runtime to see if loop should run
          If Not IsConst(EndValue) Or Not IsConst(StartValue) Then
            'If Step is an integer variable, need to check +/- first
            If StepIntVar Then
              CurrLine = LinkedListInsert(CurrLine, "IF " + StepValue + ".15 = 1 THEN" + Origin)
              CurrLine = LinkedListInsert(CurrLine, "IF " + StartValue + " < " + EndValue + " THEN" + Origin)
              IF ModePIC Then
                CurrLine = LinkedListInsert(CurrLine, " goto SysForLoopEnd" + Str(FLC))
              ElseIf ModeAVR Then
                CurrLine = LinkedListInsert(CurrLine, " rjmp SysForLoopEnd" + Str(FLC))
              End If
              CurrLine = LinkedListInsert(CurrLine, "END IF")
              CurrLine = LinkedListInsert(CurrLine, "ELSE" + Origin)
              CurrLine = LinkedListInsert(CurrLine, "IF " + StartValue + " > " + EndValue + " THEN" + Origin)
              IF ModePIC Then
                CurrLine = LinkedListInsert(CurrLine, " goto SysForLoopEnd" + Str(FLC))
              ElseIf ModeAVR Then
                CurrLine = LinkedListInsert(CurrLine, " rjmp SysForLoopEnd" + Str(FLC))
              End If
              CurrLine = LinkedListInsert(CurrLine, "END IF")
              CurrLine = LinkedListInsert(CurrLine, "END IF")
            'Step is a constant, check appropriately
            Else
              If INSTR(StepValue, "-") = 0 THEN
                CurrLine = LinkedListInsert(CurrLine, "IF " + StartValue + " > " + EndValue + " THEN" + Origin)
              Else
                CurrLine = LinkedListInsert(CurrLine, "IF " + StartValue + " < " + EndValue + " THEN" + Origin)
              End If
              IF ModePIC Then
                CurrLine = LinkedListInsert(CurrLine, " goto SysForLoopEnd" + Str(FLC))
              ElseIf ModeAVR Then
                CurrLine = LinkedListInsert(CurrLine, " rjmp SysForLoopEnd" + Str(FLC))
              End If
              CurrLine = LinkedListInsert(CurrLine, "END IF")
            End If

          End If
          CurrLine = LinkedListInsert(CurrLine, "SysForLoop" + Str(FLC) + LabelEnd)
          GetMetaData(Currline)->IsLabel = -1
          CurrLine = LinkedListInsert(CurrLine, LoopVar + "=" + LoopVar + "+" + StepValue + Origin)

      End if

      'End code
      'Find matching Next
      LoopLoc = 0
      FindLoop = CurrLine->Next
      Do While FindLoop <> 0
        InLine = UCase(FindLoop->Value)

        'Nested For, increment level counter, ensure different counter var used
        IF Left(InLine, 4) = "FOR " THEN
          FL += 1
          Temp = Mid(InLine, 5)
          Temp = Trim(Left(Temp, INSTR(Temp, "=") - 1))
          IF Temp = UCase(LoopVar) Then
            ErrorOrigin = ""
            IF INSTR(InLine, ";?F") <> 0 THEN ErrorOrigin = Mid(InLine, INSTR(InLine, ";?F"))
            Temp = Message("ForIndexInUse")
            Replace Temp, "%index%", LoopVar
            LogError Temp, ErrorOrigin
          END IF

        ElseIf Left(InLine, 8) = "EXIT FOR" AND FL = 1 Then
          IF ModePIC Then FindLoop->Value = " goto SysForLoopEnd" + Str(FLC)
          IF ModeAVR Then FindLoop->Value = " rjmp SysForLoopEnd" + Str(FLC)

        ElseIF InLine = "NEXT" Or Left(InLine, 5) = "NEXT " THEN
          FL -= 1
        END IF

        IF FL = 0 THEN LoopLoc = FindLoop: EXIT Do
        FindLoop = FindLoop->Next
      Loop

      IF LoopLoc = 0 THEN
        LogError Message("NoNext"), Origin

      Else
        LoopLoc = LinkedListDelete(LoopLoc)

        If StepExists Then

  'new NEXT code

          '#1 only add the new logic handle an integer... code only needed if an integer
          If TypeOfVar(StepValue, CompSub) = "INTEGER" then

            If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";Integer negative Step Handler in For-next statement")
            LoopLoc = LinkedListInsert(LoopLoc, "IF " + StepValue + ".15 = 1 THEN" + Origin+" ;here")

                'Pseudo code - negative ops
                '                 if LoopVar - EndValue >= abs(StepValue) then
                '                   LoopVar = LoopVar + StepValue
                '                   goto loopx
                '                 end if

                If StepIntVar Then


                  'create an tmp integer long variable - stepvalue and handle LONGs.  This prevents the maths overrun
                  'Long psuedo code
                  'Dim tmpStepValue as Integer
                  'tmpStepValue = -StepValue
                  '
                  'if LoopVar - EndValue >= tmpStepValue then
                  ' LoopVar = LoopVar - tmpStepValue
                  '
                  ' goto negloop
                  'end if


                  If TypeOfVar(LoopVar, CompSub) = "LONG" or TypeOfVar(LoopVar, CompSub) = "WORD"  then
                    AddVar "SysForLoopABsValue" + Str(FLC), "INTEGER", 1, 0, "REAL", "", , -1
                    If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";Set SysForLoopABsValue to -StepValue :#1 ")
                    LoopLoc = LinkedListInsert(LoopLoc,  "SysForLoopABsValue" + Str(FLC) + " = -([integer]" + StepValue+")")


                    If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";IF ( " + LoopVar + " - " + EndValue + ") } [WORD]" + "SysForLoopABsValue" + Str(FLC)  + " THEN  :#1n ")
                    LoopLoc = LinkedListInsert(LoopLoc,  "IF ( " + LoopVar + " - " + EndValue + ") } [WORD]" + "SysForLoopABsValue" + Str(FLC)  + " THEN" + Origin)

                  ElseIf TypeOfVar(LoopVar, CompSub) = "INTEGER"   then

                    'create a temp variable so that we can do the tests, keeping the original value as-is for maths
                    AddVar "SysForLoopABsValue" + Str(FLC), "INTEGER", 1, 0, "REAL", "", , -1
                    If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";Set SysForLoopABsValue to -StepValue#2 ")
                    LoopLoc = LinkedListInsert(LoopLoc,  "SysForLoopABsValue" + Str(FLC) + " = -([integer]" + StepValue+")")

                    if IsConst(EndValue) then
                        'create a variable to enable casting - added to resolve negative constant issue
                        AddVar "SysForLoopEndValue" + Str(FLC), "INTEGER", 1, 0, "REAL", "", , -1
                        If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";Set SysForLoopEndValue to EndValue ")
                        LoopLoc = LinkedListInsert(LoopLoc,  "SysForLoopEndValue" + Str(FLC) + " = " + str(EndValueConstantInitialValue) )
                        EndValue = "SysForLoopEndValue" + Str(FLC)
                        IntegerEndVariableCreated = -1
                    End if


                    If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";IF ( [WORD]" + LoopVar + " - [WORD]" + EndValue + ") } [WORD]" + "SysForLoopABsValue" + Str(FLC)  + " THEN :#2n ")
                    LoopLoc = LinkedListInsert(LoopLoc,     "IF ( [WORD]" + LoopVar + " - [WORD]" + EndValue + ") } [WORD]" + "SysForLoopABsValue" + Str(FLC)  + " THEN" + Origin)

                  Else
                    'If statement for NOT longs
                    If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";IF ( " + LoopVar + " - " + EndValue + ") } -" + StepValue + " THEN :#3n")
                    LoopLoc = LinkedListInsert(LoopLoc, "IF ( " + LoopVar + " - " + EndValue + ") } -" + StepValue + " THEN" + Origin)

                  End if


                else
                  'handle a constant
                  If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";IF " + EndValue + " - " + LoopVar + " ~ 0 THEN" + Origin + ":#4n")
                  LoopLoc = LinkedListInsert(LoopLoc, "IF " + EndValue + " - " + LoopVar + " ~ 0 THEN" + Origin)

                end if

                  If TypeOfVar(LoopVar, CompSub) = "LONG" then
                    If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";Set SysForLoopABsValue to LoopVar - SysForLoopABsValue  ")
                    LoopLoc = LinkedListInsert(LoopLoc, "[LONG]" + LoopVar + " = [LONG]" + LoopVar + " - " + "SysForLoopABsValue" + Str(FLC) + Origin)
                  else
                    If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";Set LoopVar to LoopVar + StepValue where StepValue is a negative value")
                    LoopLoc = LinkedListInsert(LoopLoc, LoopVar + " = " + LoopVar + " + " + StepValue + Origin + "[ao]")
                  end if

                  IF ModePIC Then
                    LoopLoc = LinkedListInsert(LoopLoc, " goto SysForLoop" + Str(FLC))
                  ElseIf ModeAVR Then
                    LoopLoc = LinkedListInsert(LoopLoc, " rjmp SysForLoop" + Str(FLC))
                  End If

                If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";END IF")
                LoopLoc = LinkedListInsert(LoopLoc, "END IF")

            LoopLoc = LinkedListInsert(LoopLoc, "ELSE" + Origin)

  'Positive handler starts here with the encoded "ELSE" above
          End if  '#1  do not handle an integer... code above is needed only when an integer

          'Pseudo code - positive or assumed constant 1
                '                  if EndValue-LoopVar >= StepValue then
                '                    LoopVar = LoopVar + StepValue
                '                    goto loopx
                '                  end if



                If StepIntVar Then
                  If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";Integer positive Step Handler in For-next statement")

                  If TypeOfVar(LoopVar, CompSub) = "WORD"  then
                    'Need to set the type to ensure operation works for integer
                    If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";IF ([WORD]" + EndValue + " - [WORD]" + LoopVar + ") } [WORD]" + StepValue + " THEN :#1p ")
                    LoopLoc = LinkedListInsert(LoopLoc,  "IF ([WORD]" + EndValue + " - [WORD]" + LoopVar + ") } [WORD]" + StepValue + " THEN" + Origin)

                 ElseIf TypeOfVar(LoopVar, CompSub) = "INTEGER" then

                    If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";INTEGER :#2pa")
                    'create a temp variable so that we can do the tests, keeping the original value as-is for maths
                    AddVar "SysForLoopABsValue" + Str(FLC), "INTEGER", 1, 0, "REAL", "", , -1
                    If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";Set SysForLoopABsValue to StepValue ")
                    LoopLoc = LinkedListInsert(LoopLoc,  "SysForLoopABsValue" + Str(FLC) + " = " + StepValue )

                    if IsConst(EndValue) or IntegerEndVariableCreated = -1 then
                        'create a variable to enable casting - added to resolve negative constant issue
                        AddVar "SysForLoopEndValue" + Str(FLC), "INTEGER", 1, 0, "REAL", "", , -1
                        If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";Set SysForLoopEndValue to EndValue :#2pb")
                        LoopLoc = LinkedListInsert(LoopLoc,  "SysForLoopEndValue" + Str(FLC) + " = " + str(EndValueConstantInitialValue))
                        EndValue = "SysForLoopEndValue" + Str(FLC)
                    End if


                    'Need to set the type to ensure operation works for integer
                    If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";IF [LONG]([INTEGER]" + EndValue + " - [INTEGER]" + LoopVar + ") } [LONG]" + "SysForLoopABsValue" + Str(FLC) + " THEN :#2pc")
                    LoopLoc = LinkedListInsert(LoopLoc,  "IF [LONG]([INTEGER]" + EndValue + " - [INTEGER]" + LoopVar + ") } [LONG]" + "SysForLoopABsValue" + Str(FLC) + " THEN" + Origin)


                  else
                    If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";IF (" + EndValue + " - " + LoopVar + ") } " + StepValue + " THEN :#3p")
                    LoopLoc = LinkedListInsert(LoopLoc, "IF (" + EndValue + " - " + LoopVar + ") } " + StepValue + " THEN" + Origin)
                  end if
                else

                  If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";Positive value Step Handler in For-next statement :#4p ")
                  LoopLoc = LinkedListInsert(LoopLoc, "IF " + EndValue + " - " + LoopVar + " ~ 0 THEN" + Origin)
                end if

                  'Finally - add the STEPVALUE!!
                  If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";Set LoopVar to LoopVar + StepValue where StepValue is a positive value")
                  LoopLoc = LinkedListInsert(LoopLoc, LoopVar + " = " + LoopVar + " + " + StepValue + Origin+ "[ao]")
                  IF ModePIC Then
                    LoopLoc = LinkedListInsert(LoopLoc, " goto SysForLoop" + Str(FLC))
                  ElseIf ModeAVR Then
                    LoopLoc = LinkedListInsert(LoopLoc, " rjmp SysForLoop" + Str(FLC))
                  End If

                If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";END IF")
                LoopLoc = LinkedListInsert(LoopLoc, "END IF")

          '#2 do not handle an integer... if an integer - close out the if statement
          If TypeOfVar(StepValue, CompSub) = "INTEGER" then
            If PreserveMode <> 0 Then LoopLoc = LinkedListInsert(LoopLoc, ";END IF")
            LoopLoc = LinkedListInsert(LoopLoc, "END IF")
          End if

        Else

            'old method - still AVAILABLE !!!
            If StepIntVar Then
              LoopLoc = LinkedListInsert(LoopLoc, "IF " + StepValue + ".15 = 1 THEN" + Origin)
              LoopLoc = LinkedListInsert(LoopLoc, "IF " + LoopVar + " > " + EndValue + " THEN" + Origin)
              IF ModePIC Then
                LoopLoc = LinkedListInsert(LoopLoc, " goto SysForLoop" + Str(FLC))
              ElseIf ModeAVR Then
                LoopLoc = LinkedListInsert(LoopLoc, " rjmp SysForLoop" + Str(FLC))
              End If
              LoopLoc = LinkedListInsert(LoopLoc, "END IF")
              LoopLoc = LinkedListInsert(LoopLoc, "ELSE" + Origin)
              LoopLoc = LinkedListInsert(LoopLoc, "IF " + LoopVar + " < " + EndValue + " THEN" + Origin)
              IF ModePIC Then
                LoopLoc = LinkedListInsert(LoopLoc, " goto SysForLoop" + Str(FLC))
              ElseIf ModeAVR Then
                LoopLoc = LinkedListInsert(LoopLoc, " rjmp SysForLoop" + Str(FLC))
              End If
              LoopLoc = LinkedListInsert(LoopLoc, "END IF")
              LoopLoc = LinkedListInsert(LoopLoc, "END IF")
            Else
              If INSTR(StepValue, "-") = 0 THEN
                LoopLoc = LinkedListInsert(LoopLoc, "IF " + LoopVar + " < " + EndValue + " THEN" + Origin)
              Else
                LoopLoc = LinkedListInsert(LoopLoc, "IF " + LoopVar + " > " + EndValue + " THEN" + Origin)
              End If
              IF ModePIC Then
                LoopLoc = LinkedListInsert(LoopLoc, " goto SysForLoop" + Str(FLC))
              ElseIf ModeAVR Then
                LoopLoc = LinkedListInsert(LoopLoc, " rjmp SysForLoop" + Str(FLC))
              End If
              LoopLoc = LinkedListInsert(LoopLoc, "END IF" + Str(FLC) + LabelEnd)
              GetMetaData(LoopLoc)->IsLabel = -1
            End If
        End If
        LoopLoc = LinkedListInsert(LoopLoc, "SysForLoopEnd" + Str(FLC) + LabelEnd)
        GetMetaData(LoopLoc)->IsLabel = -1
      End IF
    END If

    CurrLine = CurrLine->Next
  Loop

  FoundCount = FLC
  End Sub

Sub CompileGoto (CompSub As SubType Pointer)
  'Compile GOSUBs, GOTOs and labels
  Dim As String InLine, DestLabel, Origin, TempData
  'Dim As Integer PD
  Dim As LinkedListElement Pointer CurrLine, Labels
  Dim ReservedWordCounter as Integer


  FoundCount = 0

  'Find labels, alter on PICs
  If ModePIC Then
    CurrLine = CompSub->CodeStart->Next
    Do While CurrLine <> 0
      IF Right(CurrLine->Value, 1) = ":" THEN
        Replace CurrLine->Value, ":", ""
        GetMetaData(Currline)->IsLabel = -1
        FoundCount += 1
      End If
      CurrLine = CurrLine->Next
    Loop
  End If

  'Find and translate everything else
  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    InLine = UCASE(CurrLine->Value)

    If Left(InLine, 5) = "GOTO " THEN
      IF INSTR(CurrLine->Value, ";?F") <> 0 THEN
        Origin = Mid(InLine, INSTR(InLine, ";?F"))
        CurrLine->Value = RTrim(Left(CurrLine->Value, INSTR(CurrLine->Value, ";?F") - 1))
      Else
        Origin = ""
      End If
      DestLabel = Trim(MID(CurrLine->Value, 6))

      'Should check label!
      If Right(DestLabel, 1) = ":" Then DestLabel = Trim(Left(DestLabel, Len(DestLabel) - 1))

      'Check for Reservedword as label
      For ReservedWordCounter = 0 to RESERVED_WORDS
          If Ucase(Trim(DestLabel)) = Ucase(trim(ReservedWords(ReservedWordCounter)))  AND LaxSyntax = 0 THEN
              TempData = Message("CannotUseReservedWords")
              Replace TempData, "%label%", DestLabel
              LogError TempData, Origin
              exit for
          End if
      Next
      If ModePIC Then
        CurrLine->Value = " goto " + DestLabel
      ElseIf ModeAVR Then
        CurrLine->Value = " rjmp " + DestLabel
      ElseIf ModeZ8 Then
        CurrLine->Value = " jp " + DestLabel
      End If
      FoundCount += 1

    ElseIf Left(InLine, 6) = "GOSUB " THEN
      IF INSTR(CurrLine->Value, ";?F") <> 0 THEN
        Origin = Mid(InLine, INSTR(InLine, ";?F"))
        CurrLine->Value = RTrim(Left(CurrLine->Value, INSTR(CurrLine->Value, ";?F") - 1))
      Else
        Origin = ""
      End If
      DestLabel = Trim(MID(CurrLine->Value, 7))

      CurrLine->Value = " call " + DestLabel
      FoundCount += 1
    END IF
    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub CompileIF (CompSub As SubType Pointer)
  FoundCount = 0
  Dim As String Origin, Temp, L1, L2, Condition, EndBlock, LastIfOrigin
  Dim As Integer FoundIF, IL, ELC, IT, TL, FE, DelSection, DelEndIf, EndIfUsed, PrevSectionSkipped
  Dim As Integer WD, CurrIfNo

  Dim As LinkedListElement Pointer CurrLine, NewCode, FindEnd, StartDel

  'Needs to loop through program several times, compile nested IFs one level at a time
  COMPILEIFS:

  FoundIF = 0
  IL = 0

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0

    'Signalling:
    ' - DelEndIf means that one condition is always true, so all elses after should be removed
    ' - DelSection means that one condition is always false, so everything before the next else should be removed
    '   - When DelSection is used, StartDel is the first line that should be deleted by the next section

    'IF
    IF UCase(Left(CurrLine->Value, 3)) = "IF " Then

      IL = IL + 1
      IF IL = 1 THEN
        ILC = ILC + 1
        CurrIfNo = ILC
        ELC = 0

        'Get origin
        Origin = ""
        IF INSTR(CurrLine->Value, ";?F") <> 0 THEN
          Origin = Mid(CurrLine->Value, INSTR(CurrLine->Value, ";?F"))
          CurrLine->Value = RTrim(Left(CurrLine->Value, INSTR(CurrLine->Value, ";?F") - 1))
          LastIfOrigin = Origin
        END IF

        'Decide whether to jump to else or end if
        IT = 0
        TL = 1
        FindEnd = CurrLine->Next
        Do While FindEnd <> 0
          Temp = FindEnd->Value
          IF Left(Temp, 3) = "IF " Then TL += 1
          IF Left(Temp, 6) = "END IF" Then TL -= 1
          IF TL = 1 AND Left(Temp, 4) = "ELSE" Then IT = 1: Exit Do
          If TL = 0 Then Exit Do
          FindEnd = FindEnd->Next
        Loop
        'If no end found, error and quit
        If FindEnd = 0 Then
          LogError(Message("NoEndIf"), Origin)
          'Remove faulty If and continue compilation (to find further errors)
          CurrLine = LinkedListDelete(CurrLine)
          GoTo CompileIfs
        End If

        'Generate code to test and jump
        Condition = Mid(CurrLine->Value, 4)
        Condition = Trim(Left(Condition, INSTR(UCase(Condition), "THEN") - 1))
        NewCode = CompileConditions(Condition, "FALSE", Origin, CompSub)
        DelSection = 0
        DelEndIf = 0
        StartDel = 0
        EndIfUsed = 0
        If Condition = "AlwaysTrue" Then
          CurrLine = LinkedListDelete(CurrLine)
          DelEndIf = -1

        ElseIf Condition = "AlwaysFalse" Then
          DelSection = -1
          StartDel = CurrLine

        Else
          CurrLine = LinkedListDelete(CurrLine)
          CurrLine = LinkedListInsertList(CurrLine, NewCode)
          If IT = 0 Then EndBlock = "ENDIF" + Str(CurrIfNo): EndIfUsed = -1
          If IT = 1 Then EndBlock = "ELSE" + Str(CurrIfNo) + "_" + Str(ELC + 1)
          If ModePIC Then CurrLine = LinkedListInsert(CurrLine, " goto " + EndBlock)
          If ModeAVR Then CurrLine = LinkedListInsert(CurrLine, " rjmp " + EndBlock)
        End If

        FoundIF = -1
      END IF

    'Else If
    ElseIF Left(CurrLine->Value, 7) = "ELSE IF" And IL = 1 Then
      ELC += 1

      'Get origin
      Origin = ""
      IF INSTR(CurrLine->Value, ";?F") <> 0 THEN
        Origin = Mid(CurrLine->Value, INSTR(CurrLine->Value, ";?F"))
        CurrLine->Value = RTrim(Left(CurrLine->Value, INSTR(CurrLine->Value, ";?F") - 1))
      END If

      'Decide whether to jump to else or end if
      IT = 0
      TL = 1
      FindEnd = CurrLine->Next
      Do While FindEnd <> 0
        Temp = FindEnd->Value
        IF Left(Temp, 3) = "IF " Then TL += 1
        IF Left(Temp, 6) = "END IF" Then TL -= 1
        IF TL = 1 AND Left(Temp, 4) = "ELSE" Then IT = 1: Exit Do
        If TL = 0 Then Exit Do
        FindEnd = FindEnd->Next
      Loop
      'If no end found, error and quit
      If FindEnd = 0 Then
        'Print "No End If"
        LogError(Message("NoEndIf"), Origin)
        'Remove faulty If and continue compilation (to find further errors)
        CurrLine = LinkedListDelete(CurrLine)
        GoTo CompileIfs
      End If

      'Add Code
      PrevSectionSkipped = DelSection
      If DelSection Then
        CurrLine = LinkedListDeleteList(StartDel, CurrLine->Prev)->Next
        DelSection = 0
      End If
      If DelEndIf Then
        If StartDel = 0 Then
          StartDel = CurrLine
        End If
      Else
        Condition = Mid(CurrLine->Value, 8)
        Condition = Left(Condition, INSTR(UCase(Condition), "THEN") - 1)
        If PrevSectionSkipped Then
          CurrLine = LinkedListDelete(CurrLine)
        Else
          EndIfUsed = -1
          If ModePIC Then
            L1 = " goto ENDIF" + Str(CurrIfNo)
            L2 = "ELSE" + Str(CurrIfNo) + "_" + Str(ELC)

          ElseIf ModeAVR Then
            L1 = " rjmp ENDIF" + Str(CurrIfNo)
            L2 = "ELSE" + Str(CurrIfNo) + "_" + Str(ELC) + ":"

          End If
          CurrLine->Value = L1
          CurrLine = LinkedListInsert(CurrLine, L2)
          GetMetaData(Currline)->IsLabel = -1
        End If

        'Generate code to test and jump
        NewCode = CompileConditions(Condition, "FALSE", Origin, CompSub)
        DelSection = 0
        DelEndIf = 0
        StartDel = 0
        If Condition = "AlwaysTrue" Then
          'CurrLine = LinkedListDelete(CurrLine)
          DelEndIf = -1

        ElseIf Condition = "AlwaysFalse" Then
          DelSection = -1
          StartDel = CurrLine->Next

        Else
          'CurrLine = LinkedListDelete(CurrLine)
          CurrLine = LinkedListInsertList(CurrLine, NewCode)
          If IT = 0 Then EndBlock = "ENDIF" + Str(CurrIfNo): EndIfUsed = -1
          If IT = 1 Then EndBlock = "ELSE" + Str(CurrIfNo) + "_" + Str(ELC + 1)
          If ModePIC Then CurrLine = LinkedListInsert(CurrLine, " goto " + EndBlock)
          If ModeAVR Then CurrLine = LinkedListInsert(CurrLine, " rjmp " + EndBlock)
        End If

      End If

    'Else
    ElseIF (CurrLine->Value = "ELSE" Or (Left(CurrLine->Value, 4) = "ELSE" And IsDivider(Mid(CurrLine->Value, 5, 1)))) And IL = 1 Then
      'Get origin
      Origin = ""
      IF INSTR(CurrLine->Value, ";?F") <> 0 THEN
        Origin = Mid(CurrLine->Value, INSTR(CurrLine->Value, ";?F"))
        CurrLine->Value = RTrim(Left(CurrLine->Value, INSTR(CurrLine->Value, ";?F") - 1))
      END IF

      'Add Code
      PrevSectionSkipped = DelSection
      If DelSection Then
        CurrLine = LinkedListDeleteList(StartDel, CurrLine->Prev)->Next
        DelSection = 0
      End If
      If DelEndIf Then
        If StartDel = 0 Then
          StartDel = CurrLine
        End If

      Else
        If PrevSectionSkipped Then
          CurrLine = LinkedListDelete(CurrLine)

        Else
          EndIfUsed = -1
          If ModePIC Then
            L1 = " goto ENDIF" + Str(CurrIfNo)
            L2 = "ELSE" + Str(CurrIfNo) + "_" + Str(ELC + 1)

          ElseIf ModeAVR Then
            L1 = " rjmp ENDIF" + Str(CurrIfNo)
            L2 = "ELSE" + Str(CurrIfNo) + "_" + Str(ELC + 1) + ":"
          End If
          CurrLine->Value = L1
          CurrLine = LinkedListInsert(CurrLine, L2)
          GetMetaData(Currline)->IsLabel = -1
        End If

      End If

    'End If
    ElseIF UCase(Left(CurrLine->Value, 6)) = "END IF" THEN
      'Get origin
      Origin = ""
      IF INSTR(CurrLine->Value, ";?F") <> 0 THEN
        Origin = Mid(CurrLine->Value, INSTR(CurrLine->Value, ";?F"))
        CurrLine->Value = RTrim(Left(CurrLine->Value, INSTR(CurrLine->Value, ";?F") - 1))
      END IF

      'Decrement level counter, add end if label if needed
      IL = IL - 1
      IF IL = 0 Then
        If (DelSection Or DelEndIf) And StartDel <> 0 Then
          LinkedListDeleteList(StartDel, CurrLine->Prev)
        End If

        If EndIfUsed Then
          CurrLine->Value = "ENDIF" + Str(CurrIfNo) + LabelEnd
          GetMetaData(CurrLine)->IsLabel = -1
        Else
          CurrLine = LinkedListDelete(CurrLine)
        End If


      'If there are too many END IFs, display error
      ElseIf IL < 0 THEN
        IF Origin <> "" THEN
          LogError Message("ExtraENDIF"), Origin
        END IF
        IL = 0

      END IF

    END IF

    CurrLine = CurrLine->Next
  Loop

  'Not enough end ifs?
  If IL > 0 Then
    LogError(Message("NoEndIf"), LastIfOrigin)
  End If

  'Need to scan through program over and over until no more new IFs are found
  IF FoundIF THEN GOTO COMPILEIFS

  FoundCount = ILC
  End Sub

Sub CompileIntOnOff (CompSub As SubType Pointer)
  Dim As String LineTemp, TempData, Origin
  Dim As SysVarType Pointer GIEBit
  Dim NewCode(10) As String
  Dim As LinkedListElement Pointer CurrLine

  GIEBit = HashMapGet(SysVarBits, "GIE")

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    LineTemp = CurrLine->Value
    IF LEFT(LineTemp, 5) = "INTON" Or LEFT(LineTemp, 6) = "INTOFF" THEN
      Origin = ""
      IF INSTR(LineTemp, ";?F") <> 0 THEN
        Origin = Mid(LineTemp, INSTR(LineTemp, ";?F"))
        LineTemp = RTRIM(LEFT(LineTemp, INSTR(LineTemp, ";?F") - 1))
      END IF
      If LineTemp <> "INTON" And LineTemp <> "INTOFF" Then Continue Do

      If SysInt Or UserInt Then

        'Get status save variable to use (if not already found)
        If CompSub->IntStateSaveVar = "" Then
          CompSub->IntStateSaveVar = "SYSINTSTATESAVE" + Str(IntOnOffCount \ 8) + "." + Str(IntOnOffCount Mod 8)
          IntOnOffCount += 1

        End If

        CurrLine = LinkedListDelete(CurrLine)

        If ModePIC Then
          If GIEBit <> 0 Then
            If LineTemp = "INTON" Then
              'Restore state
              CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(CompSub->IntStateSaveVar, GIEBit->Parent + ".GIE", Origin))
            Else
              'Save state
              CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(GIEBit->Parent + ".GIE", CompSub->IntStateSaveVar, Origin))
              'Turn off interrupt
              CurrLine = LinkedListInsert(CurrLine, " bcf " + GIEBit->Parent + ",GIE")
            End If
          End If
        ElseIf ModeAVR Then
          If LineTemp = "INTON" Then
            'Restore state
            CurrLine = LinkedListInsertList(CurrLine, CompileConditions(CompSub->IntStateSaveVar + "=1", "TRUE", Origin))
            CurrLine = LinkedListInsert(CurrLine, " sei")
          Else
            'Save state
            CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("SREG.I", CompSub->IntStateSaveVar, Origin))
            'Turn off interrupt
            CurrLine = LinkedListInsert(CurrLine, " cli")
          End If

        End If

      Else
        'If no user or system interrupt routine, delete IntOn and IntOff
        CurrLine = LinkedListDelete(CurrLine)

      End If
    End If

    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub CompileOn (CompSub As SubType Pointer)
  Dim As String LineTemp, Origin, TempData, EnableBit, FlagBit
  Dim As String OnType, OnCondition, OnJumpTo
  Dim As String IntSource, IntEvent
  Dim As Integer IntIndex, FindIndex, HandlerSubLoc
  Dim As LinkedListElement Pointer CurrLine, OutLine
  FoundCount = 0

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0

    LineTemp = CurrLine->Value
    IF Left(LineTemp, 3) = "ON " THEN

      Origin = ""
      IF INSTR(LineTemp, ";?F") <> 0 THEN
        Origin = Mid(LineTemp, INSTR(LineTemp, ";?F"))
        LineTemp = RTRIM(LEFT(LineTemp, INSTR(LineTemp, ";?F") - 1))
      END IF

      'Get condition, type
      OnType = Mid(LineTemp, INSTR(LineTemp, " ") + 1)
      OnCondition = Trim(Mid(OnType, INSTR(OnType, " ") + 1))
      OnType = Trim(Left(OnType, INSTR(OnType, " ") - 1))

      'Get target
      If Instr(OnCondition, " CALL ") <> 0 Then
        OnJumpTo = Trim(Mid(OnCondition, Instr(OnCondition, " CALL ") + 6))
        OnCondition = Trim(Left(OnCondition, Instr(OnCondition, " CALL ") - 1))

      ElseIf Instr(OnCondition, " GOSUB ") <> 0 Then
        OnJumpTo = Trim(Mid(OnCondition, Instr(OnCondition, " GOSUB ") + 7))
        OnCondition = Trim(Left(OnCondition, Instr(OnCondition, " GOSUB ") - 1))

      ElseIf Instr(OnCondition, " IGNORE") <> 0 Then
        OnJumpTo = "IGNORE"
        OnCondition = Trim(Left(OnCondition, Instr(OnCondition, " IGNORE") - 1))

      Else
        'Error, no target
        LogError Message("MissingTarget"), Origin
        CurrLine = LinkedListDelete(CurrLine)
        Continue Do
      End If

      'On Error ...
      If OnType = "ERROR" Then
        'Implement in future version

      'On Interrupt
      ElseIf OnType = "INTERRUPT" Then
        AllowInterrupt = -1
        SysInt = -1

        'Get index
        IntIndex = 0
        FOR FindIndex = 1 to IntCount
          If Trim(UCase(Interrupts(FindIndex).EventName)) = Trim(UCase(OnCondition)) Then IntIndex = FindIndex: Exit For
        Next
        If IntIndex = 0 Then
          TempData = Message("BadIntEvent")
          Replace TempData, "%event%", OnCondition
          LogError TempData, Origin
          CurrLine = LinkedListDelete(CurrLine)
          Continue Do
        End If

        With Interrupts(IntIndex)
          'Check handler sub, request
          If OnJumpTo <> "IGNORE" Then
            HandlerSubLoc = RequestSub(0, OnJumpTo)
            If HandlerSubLoc = -1 Then
              'Error, handler Sub not found
              TempData = Message("SubNotFound")
              Replace TempData, "%sub%", OnJumpTo
              LogError TempData, Origin

              CurrLine = LinkedListDelete(CurrLine)
              Continue Do
            Else
              'If trying to jump to a function, fix call
              If Subroutine(HandlerSubLoc)->IsFunction Then
                OnJumpTo = GetSubFullName(HandlerSubLoc)
              End If
            End If
          End If

          'Show error if conflicting handler found
          If .Handler <> "" And .Handler <> OnJumpTo And OnJumpTo <> "IGNORE" Then
            TempData = Message("HandlerConflict")
            Replace TempData, "%event%", OnCondition
            LogError TempData, Origin

            CurrLine = LinkedListDelete(CurrLine)
            Continue Do
          End If

          'On PIC, generate handler routine in IntCode()
          If ModePIC Then
            If OnJumpTo = "IGNORE" Then
              If .EnableBit <> "" Then
                CurrLine->Value = "SET " + GetWholeSFR(.EnableBit) + " 0"
              Else
                CurrLine = LinkedListDelete(CurrLine)
              End If
            Else
              If .EnableBit <> "" Then
                EnableBit = GetWholeSFR(.EnableBit)
                FlagBit = GetWholeSFR(.FlagBit)
                Replace EnableBit, ".", ","
                Replace FlagBit, ".", ","

                CurrLine->Value = "SET " + GetWholeSFR(.EnableBit) + " 1"
                If .Handler = "" Then
                  If .FlagBit = "" Then
                    CurrLine = LinkedListDelete(CurrLine)
                    TempData = Message("WarningNoIntFlag")
                    Replace TempData, "%event%", .EventName
                    LogWarning TempData, Origin
                  Else
                    OutLine = IntHandlerCode->CodeEnd
                    OutLine = LinkedListInsert(OutLine, " btfss " + EnableBit)
                    OutLine = LinkedListInsert(OutLine, " goto Not" + UCase(.FlagBit))
                    OutLine = LinkedListInsert(OutLine, " btfss " + FlagBit)
                    OutLine = LinkedListInsert(OutLine, " goto Not" + UCase(.FlagBit))
                    OutLine = LinkedListInsert(OutLine, " call " + OnJumpTo)
                    OutLine = LinkedListInsert(OutLine, " bcf " + FlagBit)
                    OutLine = LinkedListInsert(OutLine, " goto INTERRUPTDONE")
                    OutLine = LinkedListInsert(OutLine, "Not" + UCase(.FlagBit))
                    IntHandlerCode->CodeEnd = OutLine
                    GetMetaData(IntHandlerCode->CodeEnd)->IsLabel = -1
                  End If
                  .Handler = OnJumpTo
                End If
              Else
                CurrLine = LinkedListDelete(CurrLine)
                TempData = Message("WarningNoIntEnable")
                Replace TempData, "%event%", .EventName
                LogWarning TempData, Origin
              End If

            End If

          'On AVR, set Interrupt().Handler
          ElseIf ModeAVR Then
            If OnJumpTo = "IGNORE" Then
              'Prog(PD) = "SET " + GetWholeSFR(.EnableBit) + " 0"
              If .EnableBit <> "" Then
                CurrLine->Value = "SET " + GetWholeSFR(.EnableBit) + " 0"
                If .FlagBit = "" Then
                  CurrLine = LinkedListDelete(CurrLine)
                  TempData = Message("WarningNoIntFlag")
                  Replace TempData, "%event%", .EventName
                  LogWarning TempData, Origin
                End If
              Else
                CurrLine = LinkedListDelete(CurrLine)
              End If
            Else
              'Prog(PD) = "SET " + GetWholeSFR(.EnableBit) + " 1"
              If .EnableBit <> "" Then
                If Val(Mid(.EnableBit,Instr(.EnableBit,".")+1)) < 8 Then
                  ' original bit code
                  CurrLine->Value = "SET " + GetWholeSFR(.EnableBit) + " 1"
                Else
                  'This is AVRDX specific but this sets the register to the bit value, rather than the set the one bit
                  'This enable the DAT to set the whole of a register ie for PORTACHANGE usage                
                  CurrLine->Value= " ldi SysValueCopy,"+ Trim(Mid(.EnableBit,Instr(.EnableBit,".")+1))
                  CurrLine = LinkedListInsert( CurrLine, "sts " + GetWholeSFR(Left(.EnableBit, Instr(.EnableBit,".")-1)) + ",SysValueCopy" )
                End If
              Else
                CurrLine = LinkedListDelete(CurrLine)
                TempData = Message("WarningNoIntEnable")
                Replace TempData, "%event%", .EventName
                LogWarning TempData, Origin
              End If
              .Handler = OnJumpTo
            End If

          End If
        End With

      'Error, bad type
      Else
        TempData = Message("BadOnType")
        Replace TempData, "%found%", OnType
        LogError TempData, Origin
        CurrLine = LinkedListDelete(CurrLine)
      End If

      FoundCount = FoundCount + 1
    END If

    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub CompilePot (CompSub As SubType Pointer)
  FoundCount = 0

  Dim As String LineTemp, Port, OutVar, Origin
  Dim As LinkedListElement Pointer CurrLine

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0

    LineTemp = CurrLine->Value
    IF Left(LineTemp, 4) = "POT " THEN
      Port = Mid(LineTemp, 5)

      Origin = ""
      IF INSTR(Port, ";?F") <> 0 THEN
        Origin = Mid(Port, INSTR(Port, ";?F"))
        Port = RTrim(Left(Port, INSTR(Port, ";?F") - 1))
      END IF

      OutVar = LTRIM(Mid(Port, INSTR(Port, ",") + 1))
      Port = Trim(Left(Port, INSTR(Port, ",") - 1))
      POC = POC + 1

      CurrLine = LinkedListDelete(CurrLine)
      CurrLine = LinkedListInsert(CurrLine, "DIR " + Port + " OUT" + Origin)
      CurrLine = LinkedListInsert(CurrLine, "SET " + Port + " 0" + Origin)
      CurrLine = LinkedListInsert(CurrLine, "WAIT 5 MS" + Origin)
      CurrLine = LinkedListInsert(CurrLine, "INTOFF")
      CurrLine = LinkedListInsert(CurrLine, "DIR " + Port + " IN" + Origin)
      CurrLine = LinkedListInsert(CurrLine, OutVar + "=0" + Origin)
      CurrLine = LinkedListInsert(CurrLine, "DO WHILE " + Port + " = 0" + Origin)
      CurrLine = LinkedListInsert(CurrLine, OutVar + "=" + OutVar + "+1" + Origin)
      CurrLine = LinkedListInsert(CurrLine, "IF " + OutVar + " = 0 THEN" + Origin)
      CurrLine = LinkedListInsert(CurrLine, "GOTO ENDPOT" + Str(POC) + Origin)
      CurrLine = LinkedListInsert(CurrLine, "END IF" + Origin)
      CurrLine = LinkedListInsert(CurrLine, "WAIT 10 10US" + Origin)
      CurrLine = LinkedListInsert(CurrLine, "LOOP " + Origin)
      CurrLine = LinkedListInsert(CurrLine, "ENDPOT" + Str(POC) + ":")
      CurrLine = LinkedListInsert(CurrLine, "INTON")

      FoundCount += 1

    END If

    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub CompileReadTable (CompSub As SubType Pointer)
  Dim As String InLine, Origin, TableName, TableLoc, OutVar, Temp
  Dim As Integer CD, TableID, TableBytes, OutBytes, CurrTableByte, ItemLoc, ItemVal
  Dim As Integer LargeTable
  Dim As LinkedListElement Pointer CurrLine
  Dim As PinDirType Pointer CurrPinDir
  FoundCount = 0

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    InLine = CurrLine->Value
    IF Left(InLine, 10) = "READTABLE " THEN

      'Get origin of line
        Origin = ""
        IF INSTR(InLine, ";?F") <> 0 THEN
          Origin = Mid(InLine, INSTR(InLine, ";?F"))
          InLine = RTrim(Left(InLine, INSTR(InLine, ";?F") - 1))
        END IF

         IF CountOccur ( Inline, ",") <> 2  THEN
            Temp = Message("InCorrectNumberofParameters")
            LogError Temp, Origin
            exit sub
         END IF

        'Get table name
        InLine = LTrim(Mid(InLine, 11))
        TableName = Trim(Left(InLine, INSTR(InLine, ",") - 1))
        InLine = Mid(InLine, INSTR(InLine, ",") + 1)

        'Get table location
        TableLoc = Trim(Left(InLine, INSTR(InLine, ",") - 1))
        InLine = Mid(InLine, INSTR(InLine, ",") + 1)

        'Get output variable
        OutVar = Trim(InLine)
        AddVar OutVar, "BYTE", 1, CompSub, "REAL", Origin

        'Check that table exists
        TableID = 0
        FOR CD = 1 to DataTables
		  ' Improved isolation for EEPROM datasets
          IF DataTable(CD).Name = UCase(Trim(TableName)) Then
            IF DataTable(CD).IsEEPromData = 0 Then
              TableID =CD
            Else
              'Must be EEPROM dataset
              TableID = 0                   
            End If
            Exit For
          End If
        NEXT

        'Table not found, show error
        IF TableID = 0 THEN
          IF DataTable(CD).IsEEPromData = 0 Then
            Temp = Message("TableNotFound")
            Replace Temp, "%Table%", TableName
            LogError Temp, Origin
            CurrLine = LinkedListDelete(CurrLine)
          Else
            Temp = Message("DataFound")
            Replace Temp, "%Table%", TableName
            LogError Temp, Origin
            CurrLine = LinkedListDelete(CurrLine)
          End If
          CurrLine = LinkedListInsert(CurrLine, origin)
        'Table found, continue compile
        Else
          'Get size of data in table
          LargeTable = 0
          If DataTable(TableID).Items > 255 Then
            LargeTable = -1
            'Upgrade type of table if necessary due to size
            If CastOrder(DataTable(TableID).Type) < CastOrder("WORD") Then
              DataTable(TableID).Type = "WORD"
            End If
          End If
          TableBytes = GetTypeSize(DataTable(TableID).Type)
          OutBytes = GetTypeSize(TypeOfVar(OutVar, CompSub))

          'Remove input line
          CurrLine = LinkedListDelete(CurrLine)

          'Write assembly
          'If reading from fixed location, remove call to table
          If IsConst(TableLoc) Then
            ItemLoc = MakeDec(TableLoc)
            With DataTable(TableID)
              If ItemLoc = 0 Then
                ItemVal = .Items
              ElseIf ItemLoc <= .Items Then
                ItemVal = .Item(ItemLoc)
              Else
                ItemVal = 0
              End If

              CurrLine = LinkedListInsert(CurrLine, OutVar + "=" + Str(ItemVal) + Origin)
            End With
          Else
            'Request table
            DataTable(TableID).Used = -1

            'Pseudo code:
            ' movf/movlw TableLoc
            ' call TableName
            ' movwf OutVar
            If ModePIC Then
              If DataTable(TableID).StoreLoc = 0 Then
                'Store in program memory
                For CurrTableByte = 1 To OutBytes
                  If CurrTableByte > TableBytes Then
                    CurrLine = LinkedListInsert(CurrLine, " clrf " + GetByte(OutVar, CurrTableByte - 1))
                  Else
                    If LargeTable Then
                      AddVar "SysStringA", "WORD", 1, CompSub, "REAL", Origin, , -1
                      CurrLine = LinkedListInsert(CurrLine, "[WORD]SYSSTRINGA=" + TableLoc + Origin)
                    Else
                      AddVar "SysStringA", "BYTE", 1, CompSub, "REAL", Origin, , -1
                      CurrLine = LinkedListInsert(CurrLine, "[BYTE]SYSSTRINGA=" + TableLoc + Origin)
                    End If

                    CurrLine = LinkedListInsert(CurrLine, " call " + GetByte(TableName, CurrTableByte - 1))
                    CurrLine = LinkedListInsert(CurrLine, " movwf " + GetByte(OutVar, CurrTableByte - 1))
                  End If
                Next

                'If writing to a port, record it
                CurrPinDir = GetPinDirection(OutVar)
                If CurrPinDir <> 0 Then
                  CurrPinDir->WrittenTo = -1
                End If

              ElseIf DataTable(TableID).StoreLoc = 1 Then
                'Store in EEPROM
      '                RequestSub(CompSub, "SysEPRead")
      '                CurrLine = LinkedListInsert(CurrLine, "EEAddress=" + TableLoc + "+@Table" + TableName)
      '                CurrLine = LinkedListInsert(CurrLine, " call SysEPRead")
      '                CurrLine = LinkedListInsert(CurrLine, OutVar + "=EEDataValue")
                    'Change SysEPRead to read the CONSTANT as this may have been redefined to an alternative method() ie K42, Q43 to NVMADR_EPREAD

                RequestSub(CompSub, ReplaceConstantsLine ( "SysEPRead", 0 ))
				'This ensures chip that require NVMADRH:NVMADRL use the WORD address
                If HasSFR("NVMADRL") Then
                  CurrLine = LinkedListInsert(CurrLine, "[WORD]SysEEAddress=" + TableLoc + "+@TABLE" + TableName)
                Else
                  CurrLine = LinkedListInsert(CurrLine, "EEAddress=" + TableLoc + "+@TABLE" + TableName)
                End if
                CurrLine = LinkedListInsert(CurrLine, " call " + ReplaceConstantsLine ( "SysEPRead", 0 ))
                CurrLine = LinkedListInsert(CurrLine, OutVar + "=EEDataValue")
              End If

          ElseIf ModeAVR Then
              If DataTable(TableID).StoreLoc = 0 Then
                'Store in program memory
                For CurrTableByte = 1 To OutBytes
                  If CurrTableByte > TableBytes Then
                    CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("0", "[BYTE]" + GetByte(OutVar, CurrTableByte - 1), Origin))
                  Else

                    If Instr( OutVar, "SysPointerX" ) > 0 Then
                      AddVar("sysbytetempa", "BYTE", 1, CompSub, "REAL", Origin, , -1)
                      AddVar("sysbytetempb", "BYTE", 1, CompSub, "REAL", Origin, , -1)
                      CurrLine = LinkedListInsert(CurrLine, ";Cache X")
                      CurrLine = LinkedListInsert(CurrLine, "[BYTE]sysbytetempa=SYSSTRINGA")
                      CurrLine = LinkedListInsert(CurrLine, "[BYTE]sysbytetempb=SYSSTRINGA_H")
                      CurrLine = LinkedListInsert(CurrLine, ";End of Cache X")
                    End If

                    AddVar("SysByteTempX", "BYTE", 1, CompSub, "REAL", Origin, , -1)

                    If LargeTable Then
                      AddVar "SysStringA", "WORD", 1, CompSub, "REAL", Origin, , -1
                      CurrLine = LinkedListInsert(CurrLine, "[WORD]SYSSTRINGA=" + TableLoc + Origin)
                    Else
                      AddVar "SysStringA", "BYTE", 1, CompSub, "REAL", Origin, , -1
                      CurrLine = LinkedListInsert(CurrLine, "[BYTE]SYSSTRINGA=" + TableLoc + Origin)
                    End If

                    CurrLine = LinkedListInsert(CurrLine, " call " + GetByte(TableName, CurrTableByte - 1))

                    If Instr( OutVar, "SysPointerX" ) > 0 Then
                      CurrLine = LinkedListInsert(CurrLine, ";Restore X")
                      CurrLine = LinkedListInsert(CurrLine, "[BYTE]SYSSTRINGA=sysbytetempa")
                      CurrLine = LinkedListInsert(CurrLine, "[BYTE]SYSSTRINGA_H=sysbytetempb")
                      CurrLine = LinkedListInsert(CurrLine, ";End of Restore X")
                    End if

                    CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("SysByteTempX", "[BYTE]" + GetByte(OutVar, CurrTableByte - 1), Origin))

                  End If
                Next

              ElseIf DataTable(TableID).StoreLoc = 1 Then
                'Store in EEPROM
                RequestSub(CompSub, "SysEPRead")
                CurrLine = LinkedListInsert(CurrLine, "EEAddress=" + TableLoc + "+@TABLE" + TableName)
                CurrLine = LinkedListInsert(CurrLine, " call SysEPRead")
                CurrLine = LinkedListInsert(CurrLine, OutVar + "=EEDataValue")
              End If

            End If
          End If
        END IF

    End IF

    CurrLine = CurrLine->Next
  Loop
  End Sub

Sub CompileRepeat (CompSub As SubType Pointer)

  Dim As String InLine, Origin, Temp, RepCount, NewOrigin, RepValType
  Dim As Integer RVN, RL, EV, FE, FS, RepNone, CheckZero, CurrByte
  Dim As LinkedListElement Pointer CurrLine, EndLoc, FindEnd
  Dim As Longint RepCountVal

  'Was RVN = 0
  'Quick and dirty fix to stop variables getting reused badly
  'set initial var number to number of repeat loops
  RVN = RPLC
  FoundCount = 0

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    InLine = CurrLine->Value
    RL = 0

    IF Left(InLine, 7) = "REPEAT " THEN

      'Get origin
      Origin = ""
      NewOrigin = ";?F0L0"
      IF INSTR(InLine, ";?F") <> 0 THEN
        Origin = Mid(InLine, INSTR(InLine, ";?F"))
        InLine = RTrim(Left(InLine, INSTR(InLine, ";?F") - 1))
        NewOrigin = Left(Origin, InStr(Origin, "S") - 1)
      END IF
      For FS = 0 To SBC
        If Subroutine(FS) = CompSub Then
          NewOrigin += "S" + Str(FS) + "?"
          Exit For
        End If
      Next

      'Get parameter
      RepCount = Trim(Mid(InLine, 7))
      If RepCount = "" Then
        LogError Message("RepeatMissingCount"), Origin
      End If

      RepValType = TypeOfValue(RepCount, CompSub)
      'If told to repeat once, counter should be byte rather than bit
      If RepValType = "BIT" Then RepValType = "BYTE"
      
      If RepValType = "SINGLE" or RepValType = "DOUBLE" Then
        LogError Message("CannotUseFloatforRepeatOperation"), Origin
      End If

      RepNone = 0
      CheckZero = -1

      'Is count zero?
      If IsConst(RepCount) Then
        CheckZero = 0
        If MakeDec(RepCount) = 0 Then
          RepNone = -1
        End If
      End If

      RL = 1
      If RepNone = 0 Then
        RPLC += 1
        RVN += 1
        AddVar "SysRepeatTemp" + Str(RVN), RepValType, 1, CompSub, "REAL", Origin, , -1
      End If

      'Pseudo code:
      'V = RV
      'SysRepeatLoop(n):
      '...
      'decfsz RV, F
      'goto SysRepeatLoop(n)

      'Starting code
      If RepNone = 0 Then
        CurrLine = LinkedListDelete(CurrLine)

        'Need to increment all outer loop counts by 1 to ensure that they repeat the correct number of times
        'If inner loop is 0, it will loop 256 times
        'Need to decrement next outer loop in this situation

        If IsConst(RepCount) And InStr(RepCount, "@") = 0 Then
          'Increment counters as needed
          RepCountVal = MakeDec(RepCount)
          For CurrByte = 1 To GetTypeSize(RepValType) - 1
            RepCountVal += (2 ^ (8 * CurrByte))
          Next
          'Decrement again to take into account 0 = 256
          For CurrByte = 0 To GetTypeSize(RepValType) - 1
            If (RepCountVal And 255 * 2 ^ (8 * CurrByte)) = 0 Then
              RepCountVal -= (2 ^ (8 * (CurrByte + 1)))
            End If
          Next
          RepCount = Str(RepCountVal)
          CurrLine = LinkedListInsert(CurrLine, "[" + RepValType + "]SysRepeatTemp" + Str(RVN) + "=" + RepCount + NewOrigin)
        Else
          CurrLine = LinkedListInsert(CurrLine, "[" + RepValType + "]SysRepeatTemp" + Str(RVN) + "=" + RepCount + NewOrigin)

          'Check for zero, don't enter loop if 0 found
          If CheckZero Then
            If ModePIC And RepValType = "BYTE" And ChipFamily <> 16 Then
              'Can't use this on 18F, movff is used for copy, which doesn't set Z
              CurrLine = LinkedListInsert(CurrLine, " btfsc STATUS,Z")
            Else
              If RepValType <> "BYTE" Then
                CurrLine = LinkedListInsertList(CurrLine, CompileConditions("[" + RepValType + "]SysRepeatTemp" + Str(RVN) + "<1", "TRUE", NewOrigin))
              Else
                CurrLine = LinkedListInsertList(CurrLine, CompileConditions("[byte]SysRepeatTemp" + Str(RVN) + "=0", "TRUE", NewOrigin))
              End If
            End If
            If ModePIC Then
              CurrLine = LinkedListInsert(CurrLine, " goto SysRepeatLoopEnd" + Str(RPLC))
            ElseIf ModeAVR Then
              CurrLine = LinkedListInsert(CurrLine, " jmp SysRepeatLoopEnd" + Str(RPLC))
            End If
          End If

          'If byte is not zero, increment next byte
          For CurrByte = 0 To GetTypeSize(RepValType) - 2
            'Can use IF and assignments here, Repeat compiled before If
            CurrLine = LinkedListInsert(CurrLine, "IF [byte]" + GetByte("SysRepeatTemp" + Str(RVN), CurrByte) + " ~ 0 THEN" + NewOrigin)
            CurrLine = LinkedListInsert(CurrLine, GetByte("SysRepeatTemp" + Str(RVN), CurrByte + 1) + "=" + GetByte("SysRepeatTemp" + Str(RVN), CurrByte + 1) + "+1" + NewOrigin)
            CurrLine = LinkedListInsert(CurrLine, "END IF" + NewOrigin)
          Next
        End If

        CurrLine = LinkedListInsert(CurrLine, "SysRepeatLoop" + Str(RPLC) + LabelEnd)
        GetMetaData(Currline)->IsLabel = -1
      End If

      'Find End
      EndLoc = 0
      FindEnd = CurrLine->Next
      Do While FindEnd <> 0
        IF Left(FindEnd->Value, 7) = "REPEAT " Then
          RL += 1
        ElseIf Left(FindEnd->Value, 10) = "END REPEAT" Then
          RL -= 1
        ElseIf Left(FindEnd->Value, 11) = "EXIT REPEAT" AND RL = 1 Then
          IF ModePIC Then FindEnd->Value = " goto SysRepeatLoopEnd" + Str(RPLC)
          IF ModeAVR Then FindEnd->Value = " rjmp SysRepeatLoopEnd" + Str(RPLC)
        End If

        IF RL = 0 THEN EndLoc = FindEnd: Exit Do
        FindEnd = FindEnd->Next
      Loop
      'Match not found error
      IF EndLoc = 0 THEN
        LogError Message("NoEndRepeat"), Origin
        Goto NextRepeat
      END IF

      'Add end code
      If RepNone Then
        'If no repeating, delete entire loop
        CurrLine = LinkedListDeleteList(CurrLine, EndLoc)

      Else
        EndLoc = LinkedListDelete(EndLoc)
        If ModePIC Then
          For CurrByte = 0 To GetTypeSize(RepValType) - 1
            EndLoc = LinkedListInsert(EndLoc, " decfsz " + GetByte("SysRepeatTemp" + Str(RVN), CurrByte) + ",F")
            EndLoc = LinkedListInsert(EndLoc, " goto SysRepeatLoop" + Str(RPLC))
          Next

        ElseIf ModeAVR Then
          Dim As String RegName
          Dim As LinkedListElement Pointer CopyToReg
          CopyToReg = LinkedListCreate
          RegName = PutInRegister(CopyToReg, "[" + RepValType + "]SysRepeatTemp" + Str(RVN), RepValType, Origin)
          EndLoc = LinkedListInsertList(EndLoc, CopyToReg)

          For CurrByte = 0 To GetTypeSize(RepValType) - 1
            EndLoc = LinkedListInsert(EndLoc, " dec " + GetByte(RegName, CurrByte))
            EndLoc = LinkedListInsertList(EndLoc, CompileVarSet("[byte]" + GetByte(RegName, CurrByte), "[byte]" + GetByte("SysRepeatTemp" + Str(RVN), CurrByte), Origin))
            EndLoc = LinkedListInsert(EndLoc, " breq PC + 2")
            EndLoc = LinkedListInsert(EndLoc, " rjmp SysRepeatLoop" + Str(RPLC))
          Next
          FreeCalcVar RegName
        End If
        EndLoc = LinkedListInsert(EndLoc, "SysRepeatLoopEnd" + Str(RPLC) + LabelEnd)
        GetMetaData(EndLoc)->IsLabel = -1
      End If

    End IF

    IF ModePIC And (INSTR(InLine, " decfsz SysRepeatTemp") <> 0 AND INSTR(InLine, "_H") = 0 AND INSTR(InLine, "_U") = 0 AND INSTR(InLine, "_E") = 0) Then RVN = RVN - 1
    If ModeAVR And (INSTR(InLine, " sts SysRepeatTemp") <> 0 AND INSTR(InLine, "_H") = 0 AND INSTR(InLine, "_U") = 0 AND INSTR(InLine, "_E") = 0) Then RVN = RVN - 1

    NextRepeat:
    CurrLine = CurrLine->Next
  Loop
  FoundCount = RPLC
  End Sub

Sub CompileReturn (CompSub As SubType Pointer)
  Dim As String InLine, Origin
  Dim As LinkedListElement Pointer CurrLine

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    InLine = UCASE(CurrLine->Value)

    'Added Instr(InLine," ") = 7  to permitted variables that start with RETURN
    'This bug was introduced at 98.03, fixes in 98.07
    'A dormat bug for a while
    If Left(InLine, 6) = "RETURN" AND Instr(Trim(InLine)," ") = 7  THEN

      'Remove origin
      IF INSTR(CurrLine->Value, ";?F") <> 0 THEN
        Origin = Mid(InLine, INSTR(InLine, ";?F"))
        CurrLine->Value = RTrim(Left(CurrLine->Value, INSTR(CurrLine->Value, ";?F") - 1))
      Else
        Origin = ""
      End If

      'If in a function, check for value after return
      Dim Value As String
      Value = Trim(Mid(CurrLine->Value, 7))

      If Value <> "" Then
        If CompSub->IsFunction Then
          LinkedListInsert(CurrLine->Prev, CompSub->Name + "=" + Value + Origin)
        Else
          LogWarning(Message("WarningReturnValueInSub"), Origin)
        End If
      End If

      If ModePIC Then
        CurrLine->Value = " return"
      ElseIf ModeAVR Then
        CurrLine->Value = " ret"
      End If

      'If this is the last line, no need to add return later
      If CurrLine->Next = 0 Then
        CompSub->NoReturn = -1
      End If

    END IF
    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub CompileRotate (CompSub As SubType Pointer)

  FoundCount = 0
  Dim As String InLine, Origin, Temp, VarName, VarType, Direction, RotateReg
  Dim As String CalcVar, AsmDir, AsmMode
  Dim As Integer UseC, OldType, FirstShift, LastShift, ShiftDir, CurrByte
  Dim As LinkedListElement Pointer CurrLine

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    InLine = UCase(CurrLine->Value)
    IF Left(InLine, 7) = "ROTATE " Then

      'Delete BASIC command, makes it a bit neater below
      CurrLine = LinkedListDelete(CurrLine)

      'Get origin
      Origin = ""
      IF INSTR(InLine, ";?F") <> 0 THEN
        Origin = Mid(InLine, INSTR(InLine, ";?F"))
        InLine = RTrim(Left(InLine, INSTR(InLine, ";?F") - 1))
      END IF

      'Get var, var type, direction, mode
      VarName = Mid(InLine, INSTR(InLine, " ") + 1)
      Direction = Mid(VarName, INSTR(VarName, " ") + 1)
      VarName = Left(VarName, INSTR(VarName, " ") - 1)
      VarType = TypeOfVar(VarName, CompSub)
      CalcVar = "Sys" + VarType + "TempA"
      IF Direction = "LC" Then Direction = "LEFT"
      IF Direction = "RC" Then Direction = "RIGHT"
      IF Direction = "L" Then Direction = "LEFT SIMPLE"
      IF Direction = "R" Then Direction = "RIGHT SIMPLE"

      'Remove cast from var name
      If InStr(VarName, "]") <> 0 Then
        VarName = LTrim(Mid(VarName, InStr(VarName, "]") + 1))
        'Detect error if cast to larger type, this will result in invalid assembly
        If GetTypeSize(TypeOfVar(VarName, CompSub)) < GetTypeSize(VarType) Then
          Temp = Message("BadCast")
          Replace Temp, "%from%", TypeOfVar(VarName, CompSub)
          Replace Temp, "%to%", VarType
          LogError Temp, Origin
          Goto CompileNextRotate
        End If
      End If

      'Check var type
      If Not IsIntType(VarType) Then 'Error
        Temp = Message("BadCommandType")
        Replace Temp, "%command%", "Rotate"
        Replace Temp, "%type%", VarType
        LogError Temp, Origin
        Goto CompileNextRotate
      End If

      'Use carry flag?
      UseC = -1
      IF INSTR(Direction, "SIMPLE") <> 0 Then
        UseC = 0
        Replace Direction, "SIMPLE", ""
        Direction = TRIM(Direction)
      End If

      'Decide how to shift
      If Direction = "LEFT" Then
        FirstShift = 0
        LastShift = GetTypeSize(VarType) - 1
        ShiftDir = 1
        AsmDir = "l"
      Else
        FirstShift = GetTypeSize(VarType) - 1
        LastShift = 0
        ShiftDir = -1
        AsmDir = "r"
      End If

      If ChipFamily = 16 Then
        AsmMode = "cf"
      Else
        AsmMode = "f"
      End If

      'Add variable
      AddVar VarName, "BYTE", 1, CompSub, "REAL", Origin

      'Pre-set C bit?
      If (Not UseC) And Not (ChipFamily = 16 And VarType = "BYTE") Then
        RotateReg = GetByte(VarName, LastShift)
        If ModeAVR And Not IsRegister(RotateReg) Then
          CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(RotateReg, "SysValueCopy", Origin))
          RotateReg = "SysValueCopy"
        End If
        If ModePIC Then
          CurrLine = LinkedListInsert(CurrLine, " r" + AsmDir + AsmMode + " " + RotateReg + ",W")
        ElseIf ModeAVR Then
          CurrLine = LinkedListInsert(CurrLine, " ro" + AsmDir + " " + RotateReg)
        End If
      End If

      'Do shift
      For CurrByte = FirstShift To LastShift Step ShiftDir
        RotateReg = GetByte(VarName, CurrByte)
        If ModeAVR And Not IsRegister(RotateReg) Then
          CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(RotateReg, "SysValueCopy", Origin))
          RotateReg = "SysValueCopy"
        End If

        'Do without carry? (PIC18F and byte only)
        If ChipFamily = 16 And VarType = "BYTE" And Not UseC Then
          CurrLine = LinkedListInsert(CurrLine, " r" + AsmDir + "ncf " + RotateReg + ",F")

        'Do with carry
        Else
          If ModePIC Then
            CurrLine = LinkedListInsert(CurrLine, " r" + AsmDir + AsmMode + " " + RotateReg + ",F")
          ElseIf ModeAVR Then
            CurrLine = LinkedListInsert(CurrLine, " ro" + AsmDir + " " + RotateReg)
            If RotateReg <> GetByte(VarName, CurrByte) Then
              CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(RotateReg, "[byte]" + GetByte(VarName, CurrByte), Origin))
            End If
          End If
        End If

      Next
      FoundCount = FoundCount + 1
    END IF

    CompileNextRotate:
    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Function CompileString (InLine As String, Origin As String) As LinkedListElement Pointer
  Dim As String DestVar, Temp, ArrayName, StringToCopy
  Dim As String Source, SourceType, SourceTable, DestArrayName
  Dim As Integer ArrayType, CD, TC, BL, SS, SArrayType, ST, CurrSubID
  Dim As VariableType Pointer SourcePtr

  Dim StringTemp(100) As String

  Dim As LinkedListElement Pointer OutList, CurrLine
  Dim As SubType Pointer CurrSub

  OutList = LinkedListCreate
  CurrLine = OutList

  'Find subroutine that contains array
  CurrSubID = GetSubID(Origin)
  CurrSub = Subroutine(CurrSubID)

  'Disable interrupt
  'CurrLine = LinkedListInsert(CurrLine, "INTOFF")

  'Get destination array
  DestVar = Trim(Left(InLine, INSTR(InLine, "=") - 1))
  ArrayName = DestVar
  If INSTR(DestVar, "(") <> 0 Then ArrayName = RTrim(Left(DestVar, INSTR(DestVar, "(") - 1))
  If INSTR(DestVar, "$") <> 0 Then ArrayName = RTrim(Left(DestVar, INSTR(DestVar, "$") - 1))
  DestArrayName = ArrayName

  'Add code to set destination array RAM pointer
  CurrLine = LinkedListInsertList(CurrLine, GenerateArrayPointerSet(DestVar, 1, CurrSub, Origin))

  'Get string to copy
  StringToCopy = Trim(Mid(InLine, INSTR(InLine, "=") + 1))
  TC = 0
  BL = 0
  Temp = ""
  For SS = 1 TO Len(StringToCopy)
    Select Case Mid(StringToCopy, SS, 1)
    Case "(":
      BL += 1
    Case ")":
      BL -= 1
    Case "+":
      If BL = 0 Then
        TC += 1: StringTemp(TC) = Trim(Temp)
        Temp = ""
      Else
        Temp = Temp + "+"
      End If
    Case Else:
      Temp = Temp + Mid(StringToCopy, SS, 1)
    End Select
  Next
  TC += 1: StringTemp(TC) = Trim(Temp)

  'Add code to clear length counter
  If TC > 1 Then
    If ModePIC Then
      CurrLine = LinkedListInsert(CurrLine, " clrf SysStringLength")
    ElseIf ModeAVR Then
      CurrLine = LinkedListInsert(CurrLine, " clr SysStringLength")
      CurrLine = LinkedListInsert(CurrLine, " ld SysValueCopy, Y+") 'Need to pre-increment Y pointer
    End If
    AddVar "SysStringLength", "BYTE", 1, 0, "REAL", Origin, , -1
  End If

  'Set string
  FOR CD = 1 to TC
    Source = StringTemp(CD)

    'Get source type
    SourceType = TypeOfValue(Source, Subroutine(GetSubID(Origin)))
    If IsConst(Source) Then SourceType = "CONST"
    IF INSTR(Source, ";STRING") <> 0 THEN SourceType = "SCONST"
    'Color 14: Print SourceType: Color 7
    Select Case SourceType

    'String
    Case "STRING":
      CurrLine = LinkedListInsertList(CurrLine, GenerateArrayPointerSet(Source, 0, CurrSub, Origin))
      RequestSub(CurrSub, "SysCopyString")
      IF TC = 1 Then CurrLine = LinkedListInsert(CurrLine, " call SYSCOPYSTRING")   'was Camelcase
      IF TC > 1 Then CurrLine = LinkedListInsert(CurrLine, " call SYSCOPYSTRINGPART")   'was Camelcase

    'String const
    Case "SCONST":
      'Add code to set ROM pointer
      ST = VAL(Mid(Source, 8))
      SourceTable = "StringTable" + Str(ST)
      StringStore(ST).Used = -1
      If ModePIC Then
        If ChipFamily <> 16 Then
          AddVar "SysStringA", "WORD", 1, 0, "REAL", Origin, , -1
          CurrLine = LinkedListInsert(CurrLine, " movlw low " + SourceTable)
          CurrLine = LinkedListInsert(CurrLine, " movwf SysStringA")
          If ChipFamily = 15 Then
            CurrLine = LinkedListInsert(CurrLine, " movlw (high " + SourceTable + ") & 127")
          Else
            CurrLine = LinkedListInsert(CurrLine, " movlw high " + SourceTable)
          End If
          CurrLine = LinkedListInsert(CurrLine, " movwf SysStringA_H")
        Else
          CurrLine = LinkedListInsert(CurrLine, " movlw low " + SourceTable)
          CurrLine = LinkedListInsert(CurrLine, " movwf TBLPTRL")
          CurrLine = LinkedListInsert(CurrLine, " movlw high " + SourceTable)
          CurrLine = LinkedListInsert(CurrLine, " movwf TBLPTRH")
          If HighTBLPTRBytes > 2 Then
            CurrLine = LinkedListInsert(CurrLine, " movlw upper " + SourceTable)
            CurrLine = LinkedListInsert(CurrLine, " movwf TBLPTRU")
          End If
        End If
      ElseIf ModeAVR Then
        AddVar "SysReadA", "WORD", 1, 0, "REAL", Origin, , -1
        CurrLine = LinkedListInsert(CurrLine, " ldi SysReadA,low(" + SourceTable + "<<1)")
        CurrLine = LinkedListInsert(CurrLine, " ldi SysReadA_H,high(" + SourceTable + "<<1)")
      End If
      RequestSub(CurrSub, "SysReadString")
      If TC = 1 Then CurrLine = LinkedListInsert(CurrLine, " call SYSREADSTRING")   'was camelcase
      If TC > 1 Then CurrLine = LinkedListInsert(CurrLine, " call SYSREADSTRINGPART") 'was camelcase

    ' single > string   floats2024
    Case "SINGLE":
          LogError "Cannot assign Single to String, use SingleToString() method", Origin

        ' Following code works... just there is no SYSSINGLETOSTRING functionality in SYSTEM.H
        'Dim As String DType, SType
        'DType = "STRING"
        'SType = SourceType

        'AddVar "Sys" + SType + "Temp", SType, 1, 0, "REAL", "", , -1
        'AddVar "Sys" + DType + "Temp", DType, 1, 0, "REAL", "", , -1
        'RequestSub(CurrSub, "Sys" + SType + "To" + DType, "")
        'CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(Source, "Sys" + SType + "Temp", Origin))
        'CurrLine = LinkedListInsert(CurrLine, " call Sys" + SType + "To" + DType)
        'CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("Sys" + DType + "Temp", Source, Origin))

    'Anything else, show error
    Case Else:
      Temp = Message("CannotConcat")
      Replace Temp, "%value%", Source
      Replace Temp, "%type%", NCase(SourceType)
      LogError Temp, Origin

    End Select

  Next

  'Set length
  IF TC > 1 Then

    CurrLine = LinkedListInsertList(CurrLine, GenerateArrayPointerSet(DestArrayName, -1, CurrSub, Origin))
    If ModePIC Then
      If ChipFamily = 16 Then
        CurrLine = LinkedListInsert(CurrLine, " movff SysStringLength, INDF0")
      ElseIf ChipFamily = 15 Then
        CurrLine = LinkedListInsert(CurrLine, " movf SysStringLength,W")
        CurrLine = LinkedListInsert(CurrLine, " movwf INDF0")
      Else
        CurrLine = LinkedListInsert(CurrLine, " movf SysStringLength,W")
        CurrLine = LinkedListInsert(CurrLine, " movwf INDF")
      End If
    ElseIf ModeAVR Then
      CurrLine = LinkedListInsert(CurrLine, " st X, SysStringLength")
    End If

  End If

  'Enable interrupt
  'CurrLine = LinkedListInsert(CurrLine, "INTON")

  Return OutList
  End Function

Sub CompileSelect (CompSub As SubType Pointer)
  FoundCount = 0
  Dim As String InLine, Origin, Temp, SelectValue, Condition, NextCaseLabel
  Dim As String ElseCaseLabel, RepValType, SCastType
  Dim As Integer PD, CC, AL, SCL, MinValue, MaxValue, AllNumeric, UseJumpTable
  Dim As Integer NumVal, HasElse, ElseCaseNo
  Dim As LinkedListElement Pointer CurrLine, NewCode, FindCase
  Dim As LinkedListElement Pointer CaseStatements, CurrCase, TablePos, SelectPos

  SCL = 0
  CaseStatements = 0

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    InLine = CurrLine->Value
    IF Left(InLine, 12) = "SELECT CASE " THEN

      Origin = ""
      IF INSTR(InLine, ";?F") <> 0 THEN
        Origin = Mid(InLine, INSTR(InLine, ";?F"))
        InLine = RTrim(Left(InLine, INSTR(InLine, ";?F") - 1))
      END IF

      FoundCount += 1

      CaseStatements = LinkedListCreate
      CurrCase = CaseStatements

      SelectValue = Trim(Mid(InLine, 12))
      If LEN(TRIM(SelectValue)) = 0 then
          LogError Message("NoSelectVariableParameter"), Origin
      End if
        
      ' Ensure SelectValue is NOT decimal
      SCastType = Mid(InLine, InStr(InLine, "[") + 1)
      SCastType = UCase(Left(SCastType, InStr(SCastType, "]") - 1))
      RepValType = TypeOfValue(SelectValue, CompSub)

      If IsConst(SelectValue) Then
          LogWarning Message("ShouldNotUseConstant"), Origin
      End If

      If CountOccur( inLine, "[") > 1 Then
        'Error handler for use of Singles.
        Dim temp As String
        temp = Message("CannotUseFloatforSelectOperation")
        Replace ( temp, "%varerror%", InLine )
        LogError temp, Origin
      End If


      SCL = 1
      CC = 0
      CurrLine = LinkedListDelete(CurrLine)

      SelectPos = CurrLine
      SCT = SCT + 1

      'Find case statements in this select case
      HasElse = 0
      ElseCaseNo = -1
      FindCase = CurrLine->Next
      Do While FindCase <> 0
        InLine = FindCase->Value

        IF Left(InLine, 12) = "SELECT CASE " THEN
          SCL += 1

        ElseIf SCL = 1 AND Left(InLine, 5) = "CASE " THEN

          'Trim trailing : from line
          If Right(InLine, 1) = ":" Then
            InLine = Left(InLine, Len(InLine) - 1)
          End If

          Condition = Trim(Mid(InLine, 5))
          IF INSTR(Condition, ";?F") <> 0 THEN
            Origin = Mid(Condition, INSTR(Condition, ";?F"))
            Condition = RTrim(Left(Condition, INSTR(Condition, ";?F") - 1))
          END If

          'Store found condition and reference to line containing case

          CurrCase = LinkedListInsert(CurrCase, Condition)
          CurrCase->MetaData = FindCase
          CC += 1

          'Anything after case else gets ignored
          If Condition = "ELSE" Then
            HasElse = -1
            ElseCaseNo = CC
          ElseIf HasElse Then
            'TODO: Warn about ignored case and remove dead code
            Color 14
            Print "Select ignoring code after Case Else"
            Color 7
          End If

        ElseIf Left(InLine, 10) = "END SELECT" THEN

          IF SCL = 1 THEN
            FindCase->Value = "SysSelectEnd" + Str(SCT) + LabelEnd
            GetMetaData(FindCase)->IsLabel = -1
          END IF
          SCL = SCL - 1

        END IF

        'Quit once End Select found
        If SCL = 0 Then Exit Do
        FindCase = FindCase->Next
      Loop

      'Empty select? Skip if yes
      If CaseStatements->Next <> 0 Then

        'Compile as jump table or as series of tests?
        'If all conditions are a number, and range is limited, use jump table
        MinValue = 65535
        MaxValue = -32768
        AllNumeric = -1
        HasElse = 0
        CurrCase = CaseStatements->Next
        Do While CurrCase <> 0
          CurrCase->NumVal = -32768
          If IsConst(CurrCase->Value) And Left(CurrCase->Value, 1) <> "@" Then
            NumVal = MakeDec(CurrCase->Value)
            If NumVal < MinValue Then MinValue = NumVal
            If NumVal > MaxValue Then MaxValue = NumVal
            CurrCase->NumVal = NumVal
          ElseIf InStr(CurrCase->Value, " TO ") <> 0 Then
            Temp = RTrim(Left(CurrCase->Value, InStr(CurrCase->Value, " TO ") - 1))
            If IsConst(Temp) And Left(Temp, 1) <> "@" Then
              NumVal = MakeDec(Temp)
              If NumVal < MinValue Then MinValue = NumVal
              If NumVal > MaxValue Then MaxValue = NumVal
            Else
              AllNumeric = 0
              Exit Do
            End If

            Temp = LTrim(Mid(CurrCase->Value, InStr(CurrCase->Value, " TO ") + 4))
            If IsConst(Temp) And Left(Temp, 1) <> "@" Then
              NumVal = MakeDec(Temp)
              If NumVal < MinValue Then MinValue = NumVal
              If NumVal > MaxValue Then MaxValue = NumVal
              CurrCase->NumVal = NumVal
            Else
              AllNumeric = 0
              Exit Do
            End If

          ElseIf CurrCase->Value = "ELSE" Then
            HasElse = -1
          Else
            AllNumeric = 0
            Exit Do
          End If

          CurrCase = CurrCase->Next
        Loop
        UseJumpTable = 0
        'Jump table takes one word per value in range plus about 14 words overhead
        'Checking individually takes about 4 words per case - which is smaller?
        If AllNumeric And (MaxValue - MinValue) < 127 And (14 + MaxValue - MinValue) < CC * 4 Then UseJumpTable = -1

        'Keep jump table mode off while problem with large Select blocks (over 2047 words on AVR, over 1023 on PIC 18F)
        If HashMapGet(Constants, "SELECT_USE_JUMP_TABLES") = 0 Then UseJumpTable = 0

        'Compile case statements (using jump table)
        If UseJumpTable Then

          'Prepare
          If ElseCaseNo = -1 Then
            ElseCaseLabel = "SysSelectEnd" + Str(SCT)

          Else
            ElseCaseLabel = "SysSelect" + Str(SCT) + "Case" + Str(ElseCaseNo)
          End If

          'Generate jump table
          TablePos = SelectPos
          'Check select value, jump to else if too high
          If ModePIC Then
            If MinValue = 0 Then
              TablePos = LinkedListInsert(TablePos, "SYSBYTETEMPA=" + SelectValue)
            Else
              TablePos = LinkedListInsert(TablePos, "SYSBYTETEMPA=" + SelectValue + "-" + Str(MinValue))
            End If
            TablePos = LinkedListInsertList(TablePos, CompileConditions("SYSBYTETEMPA>" + Str(MaxValue - MinValue), "TRUE", Origin, CompSub))
            TablePos = LinkedListInsert(TablePos, " goto " + ElseCaseLabel)
          ElseIf ModeAVR Then
            If MinValue = 0 Then
              TablePos = LinkedListInsert(TablePos, "SYSSTRINGLENGTH=" + SelectValue)
            Else
              TablePos = LinkedListInsert(TablePos, "SYSSTRINGLENGTH=" + SelectValue + "-" + Str(MinValue))
            End If
            TablePos = LinkedListInsertList(TablePos, CompileConditions("SYSSTRINGLENGTH>" + Str(MaxValue - MinValue), "TRUE", Origin, CompSub))
            TablePos = LinkedListInsert(TablePos, " rjmp " + ElseCaseLabel)
          End If
          'Generate computed goto
          If ModePIC Then
            If ChipFamily = 12 THEN
              TablePos = LinkedListInsert(TablePos, " movf SYSBYTETEMPA, W")
              TablePos = LinkedListInsert(TablePos, " addwf PCL, F")

            ElseIf ChipFamily = 15 Then
              TablePos = LinkedListInsert(TablePos, " movf SYSBYTETEMPA, W")
              TablePos = LinkedListInsert(TablePos, " brw")

            ElseIf ChipFamily = 14 Or ChipFamily = 16 Then
              If ChipFamily = 14 Then
                TablePos = LinkedListInsert(TablePos, " movf SYSBYTETEMPA, W")
              Else
                TablePos = LinkedListInsert(TablePos, " rlcf SYSBYTETEMPA, W")
              End If
              TablePos = LinkedListInsert(TablePos, " addlw low SysSelect" + Str(SCT) + "Vectors")
              TablePos = LinkedListInsert(TablePos, " movwf SYSBYTETEMPA")
              TablePos = LinkedListInsert(TablePos, " movlw high SysSelect" + Str(SCT) + "Vectors")
              TablePos = LinkedListInsert(TablePos, " btfsc STATUS, C")
              TablePos = LinkedListInsert(TablePos, " addlw 1")
              TablePos = LinkedListInsert(TablePos, " movwf PCLATH")
              TablePos = LinkedListInsert(TablePos, " movf SYSBYTETEMPA, W")
              TablePos = LinkedListInsert(TablePos, " movwf PCL")
              TablePos = LinkedListInsert(TablePos, "SysSelect" + Str(SCT) + "Vectors")
            End If
          ElseIf ModeAVR Then
            TablePos = LinkedListInsert(TablePos, " ldi SysReadA, low(SysSelect" + Str(SCT) + "Vectors)")
            TablePos = LinkedListInsert(TablePos, " ldi SysReadA_H, high(SysSelect" + Str(SCT) + "Vectors)")
            TablePos = LinkedListInsert(TablePos, " add SysReadA, SYSSTRINGLENGTH")
            TablePos = LinkedListInsert(TablePos, " brcc PC + 2")
            TablePos = LinkedListInsert(TablePos, " inc SysReadA_H")

            TablePos = LinkedListInsert(TablePos, " ijmp")
            TablePos = LinkedListInsert(TablePos, "SysSelect" + Str(SCT) + "Vectors:")

            AddVar "SysStringA", "BYTE", 1, 0, "REAL", "", , -1
            AddVar "SysReadA", "WORD", 1, 0, "REAL", "", , -1
          End If

          'Generate labels to jump to
          For NumVal = MinValue To MaxValue
            CC = 0
            CurrCase = CaseStatements->Next
            Do While CurrCase <> 0
              CC += 1
              FindCase = CurrCase->MetaData
              Condition = CurrCase->Value

              If InStr(Condition, " TO ") <> 0 Then
                Temp = RTrim(Left(CurrCase->Value, InStr(CurrCase->Value, " TO ") - 1))
                Condition = LTrim(Mid(CurrCase->Value, InStr(CurrCase->Value, " TO ") + 4))
                If NumVal < MakeDec(Temp) Or NumVal > MakeDec(Condition) Then GoTo CheckNextCase
              Else
                If NumVal <> CurrCase->NumVal Then GoTo CheckNextCase
              End If

              'Found correct case
              'TODO: Make this work if one of the jumps has to go a long way
              If ModePIC Then
                If ChipFamily = 16 Then
                  TablePos = LinkedListInsert(TablePos, " bra SysSelect" + Str(SCT) + "Case" + Str(CC))
                Else
                  TablePos = LinkedListInsert(TablePos, " goto SysSelect" + Str(SCT) + "Case" + Str(CC))
                End If
              ElseIf ModeAVR Then
                TablePos = LinkedListInsert(TablePos, " rjmp SysSelect" + Str(SCT) + "Case" + Str(CC))
              End If

              Exit Do
              CheckNextCase:
              CurrCase = CurrCase->Next
            Loop
          Next

          'Process case statements
          CC = 0
          CurrCase = CaseStatements->Next
          Do While CurrCase <> 0
            CC += 1
            FindCase = CurrCase->MetaData

            If CC = 1 THEN
              FindCase->Value = "SysSelect" + Str(SCT) + "Case" + Str(CC) + LabelEnd
            Else
              If ModePIC Then
                FindCase->Value = " goto SysSelectEnd" + Str(SCT)
                FindCase = LinkedListInsert(FindCase, "SysSelect" + Str(SCT) + "Case" + Str(CC))
              ElseIf ModeAVR Then
                FindCase->Value = " rjmp SysSelectEnd" + Str(SCT)
                FindCase = LinkedListInsert(FindCase, "SysSelect" + Str(SCT) + "Case" + Str(CC) + ":")
              End If
            END If

            CurrCase = CurrCase->Next
          Loop

        'Compile case statements (using sequential tests)
        Else
          CC = 0
          CurrCase = CaseStatements->Next
          Do While CurrCase <> 0
            CC += 1
            FindCase = CurrCase->MetaData
            Condition = CurrCase->Value
            If CurrCase->Next = 0 Then
              NextCaseLabel = "SysSelectEnd" + Str(SCT) 'seems to be for select greater than 1
            Else
              NextCaseLabel = "SysSelect" + Str(SCT) + "Case" + Str(CC + 1)
              GetMetaData(FindCase)->IsLabel = -1
            End If

            If CC = 1 THEN
              FindCase->Value = "SysSelect" + Str(SCT) + "Case" + Str(CC) + LabelEnd
              GetMetaData(FindCase)->IsLabel = -1
            Else
              If ModePIC Then
                FindCase->Value = " goto SysSelectEnd" + Str(SCT)
                GetMetaData(FindCase)->IsLabel = 0
                FindCase = LinkedListInsert(FindCase, "SysSelect" + Str(SCT) + "Case" + Str(CC))
                GetMetaData(FindCase)->IsLabel = -1
              ElseIf ModeAVR Then
                FindCase->Value = " rjmp SysSelectEnd" + Str(SCT)
                FindCase = LinkedListInsert(FindCase, "SysSelect" + Str(SCT) + "Case" + Str(CC) + ":")
              End If
            END If

            IF Condition <> "ELSE" THEN
              'Prepare condition test
              If InStr(Condition, " TO ") <> 0 Then
                'Test range
                Temp = RTrim(Left(CurrCase->Value, InStr(CurrCase->Value, " TO ") - 1))
                Condition = LTrim(Mid(CurrCase->Value, InStr(CurrCase->Value, " TO ") + 4))

                'Test lower
                NewCode = CompileConditions(SelectValue + "<" + Temp, "TRUE", Origin, CompSub)
                FindCase = LinkedListInsertList(FindCase, NewCode)
                If ModePIC Then
                  FindCase = LinkedListInsert(FindCase, " goto " + NextCaseLabel)
                ElseIf ModeAVR Then
                  FindCase = LinkedListInsert(FindCase, " rjmp " + NextCaseLabel)
                End If
                'Test upper
                NewCode = CompileConditions(SelectValue + ">" + Condition, "TRUE", Origin, CompSub)
                FindCase = LinkedListInsertList(FindCase, NewCode)
                If ModePIC Then
                  FindCase = LinkedListInsert(FindCase, " goto " + NextCaseLabel)
                ElseIf ModeAVR Then
                  FindCase = LinkedListInsert(FindCase, " rjmp " + NextCaseLabel)
                End If

              Else
                'Only one value to test
                IF CountOccur(Condition, "';=~<>{}") = 0 THEN Condition = "=" + Condition
                Condition = SelectValue + Condition
                NewCode = CompileConditions(Condition, "FALSE", Origin, CompSub)
                FindCase = LinkedListInsertList(FindCase, NewCode)
                If ModePIC Then
                  FindCase = LinkedListInsert(FindCase, " goto " + NextCaseLabel)
                ElseIf ModeAVR Then
                  FindCase = LinkedListInsert(FindCase, " rjmp " + NextCaseLabel)
                End If
              End If

            END If

            CurrCase = CurrCase->Next
          Loop
        End If
      END If

      If CaseStatements <> 0 Then
        LinkedListDeleteList(CaseStatements, 0)
      EndIf

    End If

    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub CompileSet (CompSub As SubType Pointer)
  FoundCount = 0
  Dim As String InLine, Origin, Temp
  Dim As String VarName, VarBit, Status

  Dim As String Token(20)
  Dim As Integer Tokens

  Dim As LinkedListElement Pointer CurrLine
  Dim As PinDirType Pointer CurrPinDir

  'Find SET commands
  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0

    InLine = CurrLine->Value
    IF UCase(Left(InLine, 4)) = "SET " THEN

      'Get origin
      Origin = ""
      IF INSTR(InLine, ";?F") <> 0 THEN
        Origin = Mid(InLine, INSTR(InLine, ";?F"))
        InLine = RTrim(Left(InLine, INSTR(InLine, ";?F") - 1))
      END IF

      'Get Tokens from line
      GetTokens(InLine, Token(), Tokens)
      'Should be: 1 = SET, 2 = Var, 3 = Bit, 4 = Status

      'Delete line from program, will add asm later
      CurrLine = LinkedListDelete(CurrLine)

      'Do we have a bit by itself?
      If Tokens = 3 Then
        VarName = GetWholeSFR(Token(2))
        If InStr(VarName, ".") <> 0 Then
          'Found valid bit, correct command
          Tokens = 4
          Token(4) = Token(3)
          Token(2) = Left(VarName, InStr(VarName, ".") - 1)
          Token(3) = Mid(VarName, InStr(VarName, ".") + 1)
        End If
      End If

      'Get var/port and bit
      If Tokens = 4 Then
        VarName = Token(2)
        VarBit = Token(3)
        Status = Token(4)

        'If writing to a pin, record it
        CurrPinDir = GetPinDirection(VarName + "." + VarBit)
        If CurrPinDir <> 0 Then
          CurrPinDir->WrittenTo = -1
        End If

      Else
        'Wrong number of input parameters
        Temp = Message("BadParamsSet")
        LogError Temp, Origin
        GOTO BADSET
      End If

      'Generate code
      CurrLine = LinkedListInsertList(CurrLine, GenerateBitSet(VarName + "." + VarBit, Status, Origin, CompSub))

      FoundCount = FoundCount + 1
    END IF

  BADSET:
    CurrLine = CurrLine->Next
  Loop
  End Sub

Function CompileSubCall (InCall As SubCallType Pointer) As LinkedListElement Pointer

  Dim As Integer CD, C, F, L, S, AF, StringConstCount, RP, ParamIsFn, LocOfFn, ST
  Dim As Integer StringConstLen
  Dim As String Temp, SendOrigin, ReceiveOrigin, SourceArray, DestArray
  Dim As String ArrayHandler, SourceArrayHandler, TempData, NextLine, GenName
  Dim As String CallCmd, MacroLineOrigin, SourceFunction
  Dim As LinkedListElement Pointer BeforeCode, AfterCode, BeforePos, AfterPos
  Dim As VariableType Pointer SourcePtr, SourceArrayPtr

  Dim As LinkedListElement Pointer OutList, CurrPos, MacroLine
  OutList = LinkedListCreate

  'Temporary code lists
  BeforeCode = LinkedListCreate
  AfterCode = LinkedListCreate
  BeforePos = BeforeCode
  AfterPos = AfterCode

  StringConstCount = 0

  'Load code to call Sub into OutList
  With (*InCall)
    If .Called->IsMacro Then
      'Dealing with a macro
      'Adjust source as needed
      TidyInputSource(.Called)

      'Copy lines over, replacing params where needed
      CurrPos = OutList
      If PreserveMode = 2 Then
        PCC += 1
        PreserveCode(PCC) = ";Macro Source: " + GetFileLine(.Called->Origin)
        CurrPos = LinkedListInsert(CurrPos, "PRESERVE " + Str(PCC))
      End If

      MacroLine = .Called->CodeStart->Next
      Do While MacroLine <> 0
        TempData = MacroLine->Value
        'Remove original origin
        MacroLineOrigin = ""
        IF INSTR(TempData, ";?F") <> 0 THEN
          MacroLineOrigin = " " + Mid(TempData, INSTR(TempData, ";?F"))
          TempData = RTrim(Left(TempData, INSTR(TempData, ";?F") - 1))
          'If macro line has an origin, check if dest line also has one
          If .Origin <> "" Then
            'If dest line has origin, use that instead of macro line's original
            MacroLineOrigin = " " + Trim(.Origin)
          End If
        END If
        'Replace parameters
        For RP = 1 to .Params
          'Change 1205 add the IF check to stop recursive replacement  - so, the IF and ENDIF
          If .Called->Params(RP).Name <> .Param(RP, 1) Then
            WholeReplace TempData, .Called->Params(RP).Name, .Param(RP, 1)   'this was the original line. When the two params are the same then the compiler locked up as it never exits WholeReplace()
          Else
            Temp = Message("UndeclaredMacroVar")
            Replace Temp, "%var%", .Param(RP, 1)
            LogError Temp, .Origin
          End If
        Next
        'Hide macro name (if macro is a function)
        If .Called->IsFunction Then
          Do While InStr(TempData, .Called->Name) <> 0
            Replace (TempData, .Called->Name, Chr(31) + Str((*InCall).CalledID) + Chr(31))
          Loop
        End If
        'Insert line (or lines, divided by :)
        If TempData <> "" Then

          Do While InStr(TempData, ":") <> 0
            NextLine = Trim(Mid(TempData, InStr(TempData, ":") + 1))
            'If line is label, : should be last character
            If NextLine <> "" Then
              CurrPos = LinkedListInsert(CurrPos, Trim(Left(TempData, InStr(TempData, ":") - 1)) + MacroLineOrigin)
              TempData = NextLine
            End If
          Loop

          'Append insertion origin
          If TempData <> "" Then
            CurrPos = LinkedListInsert(CurrPos, TempData + MacroLineOrigin)
          End If
        End If
        MacroLine = MacroLine->Next
      Loop
      Return OutList

    Else
      'Not dealing with a macro, so set parameters and call
      'Get call command
      With (*.Called)
        If .IsFunction Then
          'GenName = "FN_" + .Name
          GenName = "FN_" + Chr(31) + Str((*InCall).CalledID) + Chr(31)
        Else
          'GenName = .Name
          GenName = Chr(31) + Str((*InCall).CalledID) + Chr(31)
        End If
        IF .Overloaded Then
          CallCmd = " call " + GenName + Str((*InCall).CalledID)
        Else
          CallCmd = " call " + GenName
        End If
        'Print "Compiling call to " + .Name + " (from " + InCall->Caller->Name + ")"
      End With

      'Optional params
      If (*.Called).ParamCount > .Params Then
        For CD = .Params + 1 To (*.Called).ParamCount
          .Param(CD, 1) = ""
        Next
        .Params = (*.Called).ParamCount
      End If

      'Make code to copy parameters
      FOR CD = 1 TO .Params

        'Load defaults into optional parameters
        IF (*.Called).Params(CD).Default <> "" And .Param(CD, 1) = "" Then
          .Param(CD, 1) = (*.Called).Params(CD).Default

          'Constants won't have been replaced in default values
          .Param(CD, 1) = ReplaceConstantsLine(.Param(CD, 1), 0)
          .Param(CD, 2) = TypeOfValue(.Param(CD, 1), Subroutine(GetSubID(.Origin)))
        End If

        'Decide how to pass parameter
        'C = 0: Copy variable
        'C = 1: Copy constant value
        'C = 2: Copy calculation result
        'C = 3: Copy string/array reference
        'C = 4: Copy string into temp array then pass reference
        'C = 5: Concatenate strings, store in temp array, pass reference
        'C = -1: Error
        If .Param(CD, 1) <> "" Then
          C = 0
          IF IsConst(.Param(CD, 1)) THEN C = 1
          IF IsCalc(.Param(CD, 1)) THEN C = 2
          IF INSTR((*.Called).Params(CD).Type, "$") <> 0 Or INSTR((*.Called).Params(CD).Type, "()") <> 0 Or (*.Called).Params(CD).Type = "STRING" Then
            C = 3
            If InStr(.Param(CD, 1), "+") Then C = 5
          End If
          'IF INSTR(.Param(CD, 1), ";STRING") <> 0 THEN
          'Only search for ;STRING at start, not in middle!
          IF INSTR(.Param(CD, 1), ";STRING") = 1 Then
            Temp = StringStore(Val(MID(.Param(CD, 1), Instr(.Param(CD, 1), ";STRING") + 7))).Value
            If Len(Temp) > 1 Or (INSTR((*.Called).Params(CD).Type, "$") <> 0 Or INSTR((*.Called).Params(CD).Type, "()") <> 0 Or (*.Called).Params(CD).Type = "STRING") Then
              C = 4
              If InStr(.Param(CD, 1), "+") Then C = 5
            Else
              C = 1
            End If
          End If
        Else
          Temp = Message("MissingSubParam")
          Replace Temp, "%param%", (*.Called).Params(CD).Name
          LogError Temp, .Origin
          C = -1
        End If
        'Print , "Copying "; .Param(CD, 1); " to "; *.Called.Params(CD).Name; " using mode "; C

        'Pass by copying
        If C = 0 OR C = 1 OR C = 2 THEN
          'Calculate new origins
          'Important so that variable types are detected correctly
          SendOrigin = ""
          ReceiveOrigin = ""
          If .Origin <> "" Then
            F = VAL(Mid(.Origin, INSTR(.Origin, "F") + 1))
            L = VAL(Mid(.Origin, INSTR(.Origin, "L") + 1))
            Temp = Mid(.Origin, INSTR(.Origin, "S") + 1)
            IF INSTR(Temp, "D") <> 0 Then Temp = Left(Temp, INSTR(Temp, "D") - 1)
            S = VAL(Temp)
          Else
            'If no origin present, make one up so variable assignments work
            'As long as Sub (S) is correct, it will work properly
            '(except for maybe giving misleading error locations)
            F = 0
            L = 0
            S = 0
            For LocOfFn = 0 To SBC
              If Subroutine(LocOfFn) = .Caller Then
                S = LocOfFn
                Exit For
              End If
            Next
          End If
          SendOrigin = ";?F" + Str(F) + "L" + Str(L) + "S" + Str(S) + "D" + Str(.CalledID) + "?"
          ReceiveOrigin = ";?F" + Str(F) + "L" + Str(L) + "S" + Str(.CalledID) + "D" + Str(S) + "?"

          If ((*.Called).Params(CD).Dir And 1) <> 0 Then
            BeforePos = LinkedListInsert(BeforePos, (*.Called).Params(CD).Name + " = " + .Param(CD, 1) + SendOrigin)
          End If
          'Detect parameters that are functions, don't try returning values in them
          '(Unless parameter is the current function, in which case treat it as a variable)
          ParamIsFn = 0
          LocOfFn = LocationOfSub(.Param(CD, 1), "", .Origin, -1)
          If LocOfFn <> 0 And LocOfFn <> S Then
            If Subroutine(LocOfFn)->IsFunction Then ParamIsFn = -1
          End If
          IF C = 0 AND ((*.Called).Params(CD).Dir And 2) <> 0 And Not ParamIsFn THEN
            AfterPos = LinkedListInsert(AfterPos, .Param(CD, 1) + " = " + (*.Called).Params(CD).Name + ReceiveOrigin)
          ElseIf (*.Called).Params(CD).Dir = 2 And C <> 0 Then
            'Error, need to supply output
            Temp = Message("SubParamNotVar")
            Replace Temp, "%value%", .Param(CD, 1)
            Replace Temp, "%param%", (*.Called).Name
            LogError Temp, .Origin
          End IF
        END IF

        'Pass string using SYSTEMPARRAY
        IF C = 4 Or C = 5 THEN
          'On 16F1, should pass reference to string location if Sub will not be changing it
          If C = 4 And ChipFamily = 15 And (*.Called).Params(CD).Dir = 1 Then
            'Print "Opportunity for optimisation: "; *.Called.Params(CD).Name; " = "; .Param(CD, 1)
            'Destination array
            DestArray = (*.Called).Params(CD).Name
            IF INSTR(DestArray, "()") <> 0 THEN DestArray = Left(DestArray, INSTR(DestArray, "()") - 1)
            IF INSTR(DestArray, "$") <> 0 THEN DestArray = Left(DestArray, INSTR(DestArray, "$") - 1)
            'Create destination array if necessary
            SourcePtr = VarAddress(DestArray, .Called)
            AddVar DestArray, "BYTE", 2, .Called, "POINTER", .Origin
            'Set handler name
            ArrayHandler = "Sys" + DestArray + "Handler"
            AddVar ArrayHandler, "WORD", 1, 0, "REAL", .Origin, , -1 'Make handler global
            'Copy reference
            ST = VAL(Mid(.Param(CD, 1), 8))
            StringStore(ST).Used = -1
            BeforePos = LinkedListInsert(BeforePos, " movlw low StringTable" + Str(ST))
            BeforePos = LinkedListInsert(BeforePos, " movwf " + ArrayHandler)
            BeforePos = LinkedListInsert(BeforePos, " movlw (high StringTable" + Str(ST) + ") | 128")
            BeforePos = LinkedListInsert(BeforePos, " movwf " + ArrayHandler + "_H")

          'In other situations, copy string constant into temporary variable, then pass reference to that
          Else
            StringConstLen = Len(GetString(.Param(CD, 1), 0))
            StringConstCount += 1

           'Start of #1039 - Concat changes
           'print StringConstLen, *.Called.Params(CD).Name, .Param(CD, 1)
           'Need to ensure a concat is resolved
            StringConCatLengthAdapted = 0
           'Declare a local variables
            Dim As String  StringConCatString = ""

            If HashMapGet(Constants, "SYSDEFAULTCONCATSTRING" ) > 0  then
              'Always use this if defined constant
              StringConstLen = val(HashMapGetStr(Constants, "SYSDEFAULTCONCATSTRING" ))
              StringConCatString = ";Used SYSDEFAULTCONCATSTRING constant to create SYSSTRINGPARAM"+ str(StringConstCount)+" string of length " + str(StringConstLen) + " to cater for constant_string + constant_string Concatenation"
              StringConCatLengthAdapted = -1

            End If

            If PreserveMode <> 0 Then
              If StringConCatLengthAdapted = -1 then
                  LinkedListInsert(BeforePos,StringConCatString)
              Else
                  LinkedListInsert(BeforePos,";Using SYSSTRINGPARAM"+Str(StringConstCount)+"*"+str(StringConstLen))
              End if
            End If
            'End of #1039 - Concat changes

            BeforePos = LinkedListInsert(BeforePos, "SYSSTRINGPARAM" + Str(StringConstCount) + "=" + .Param(CD, 1) + .Origin)
            .Param(CD, 1) = "SYSSTRINGPARAM" + Str(StringConstCount)
            .Param(CD, 2) = "STRING"
            AddVar("SYSSTRINGPARAM" + Str(StringConstCount), "STRING*" + Str(StringConstLen), StringConstLen, Subroutine(0), "REAL", .Origin, , -1)
            'Record use of temporary variable
            LinkedListInsert(.Caller->TemporaryVars, "SYSSTRINGPARAM" + Str(StringConstCount))
            C = 3
          End If
        END IF

        'Pass array by reference (set pointer)
        IF C = 3 THEN
          'Source
          SourceArray = .Param(CD, 1)
          'IF INSTR(SourceArray, "(") <> 0 THEN SourceArray = Left(SourceArray, INSTR(SourceArray, "(") - 1)
          IF INSTR(SourceArray, "()") <> 0 THEN SourceArray = RTrim(Left(SourceArray, INSTR(SourceArray, "()") - 1))
          IF INSTR(SourceArray, "$") <> 0 THEN SourceArray = RTrim(Left(SourceArray, INSTR(SourceArray, "$") - 1))

          'Is SourceArray a function?
          LocOfFn = LocationOfSub(.Param(CD, 1), "", .Origin, -1)

          'SourceArray is function
          If LocOfFn <> 0 Then
            SourceFunction = SourceArray
            IF INSTR(SourceArray, "(") <> 0 THEN SourceArray = RTrim(Left(SourceArray, INSTR(SourceArray, "(") - 1))
            SourceArrayPtr = VarAddress(ReplaceFnNames(SourceArray), .Caller)
            If SourceArrayPtr = Cast( VariableType Pointer, INVALIDARRAYVALUE )Then
              'this traps the error in VarAddress()
              GoTo CompileNextParam
            End If
            If SourceArrayPtr = 0 Then
              'This should never run
  '              Color 12
  '              Print "Internal error in CompileSubCall: A GCBASIC library method " + ReplaceFnNames(SourceArray) + " exists but the return variable does not. Is this a call to a Subroutine rather than a Function? "
  '              Print "(in " + .Caller->Name + ", calling " + SourceArray + ")"
  '              Color 7
              Dim temp as String
              Temp = Message( "CannotHandleFunctionCall")
              Replace ( Temp, "%fn" , ReplaceFnNames(SourceArray) )
              LogError Temp, .Origin
              GoTo CompileNextParam
            End If

            'Remove Function name from SourceArray
            SourceArray = Chr(31) + Str(LocOfFn) + Chr(31)

          'SourceArray is not a function
          Else
            SourceFunction = SourceArray
            SourceArrayPtr = VarAddress(ReplaceFnNames(SourceArray), .Caller)
            If Cast( Integer, SourceArrayPtr ) = INVALIDARRAYVALUE Then
                Dim Temp as String
                Temp = Message("CannotHandleArrayConstruction")
                LogError Temp, .Origin
                GoTo CompileNextParam
            End If
            'If source array not found, show error
            If SourceArrayPtr = 0 Then
              Temp = Message("ArrayNoDec")
              Replace Temp, "%name%", ReplaceFnNames(SourceArray)
              LogError Temp, .Origin
              GoTo CompileNextParam
            Else
              if left( ucase( trim(ReplaceFnNames(SourceArray) )),1)  = "(" AND right( ucase( trim(ReplaceFnNames(SourceArray) )),1)  = ")" then
                  'This should never run
                   Temp = Message("DoubleBracesError")
                   Replace Temp, "%source%", ReplaceFnNames(SourceArray)
                   LogError Temp, .Origin
                   GoTo CompileNextParam
              End if
              'Mark source variable as used
              RequestVariable(ReplaceFnNames(SourceArray), .Caller)
            End If
          End If

          'Destination
          DestArray = (*.Called).Params(CD).Name
          'IF INSTR(DestArray, "(") <> 0 THEN DestArray = Left(DestArray, INSTR(DestArray, "(") - 1)
          IF INSTR(DestArray, "()") <> 0 THEN DestArray = Left(DestArray, INSTR(DestArray, "()") - 1)
          IF INSTR(DestArray, "$") <> 0 THEN DestArray = Left(DestArray, INSTR(DestArray, "$") - 1)

          'Create destination array if necessary
          SourcePtr = VarAddress(DestArray, .Called)
          AddVar DestArray, "BYTE", 2, .Called, "POINTER", .Origin

          'Set handler name
          ArrayHandler = "Sys" + DestArray + "Handler"
          AddVar ArrayHandler, "WORD", 1, 0, "REAL", .Origin, , -1 'Make handler global

          'May need to hide Function names in array names
          Do While InStr(SourceArray, .Called->Name) <> 0
            Replace (SourceArray, .Called->Name, Chr(31) + Str((*InCall).CalledID) + Chr(31))
          Loop
          Do While InStr(ArrayHandler, .Called->Name) <> 0
            Replace (ArrayHandler, .Called->Name, Chr(31) + Str((*InCall).CalledID) + Chr(31))
          Loop
          SourceArrayHandler = "Sys" + SourceArray + "Handler"

          If ModePIC Then
            If SourceArrayPtr->Pointer = "POINTER" Then
              If ChipFamily = 16 Then
                BeforePos = LinkedListInsert(BeforePos, " movff " + SourceArrayHandler + "," + ArrayHandler)
                BeforePos = LinkedListInsert(BeforePos, " movff " + SourceArrayHandler + "_H," + ArrayHandler + "_H")
              Else
                BeforePos = LinkedListInsert(BeforePos, " movf " + SourceArrayHandler + ",W")
                BeforePos = LinkedListInsert(BeforePos, " movwf " + ArrayHandler)
                BeforePos = LinkedListInsert(BeforePos, " movf " + SourceArrayHandler + "_H,W")
                BeforePos = LinkedListInsert(BeforePos, " movwf " + ArrayHandler + "_H")
              End If
            Else
              BeforePos = LinkedListInsert(BeforePos, " movlw low " + SourceFunction)
              BeforePos = LinkedListInsert(BeforePos, " movwf " + ArrayHandler)
              BeforePos = LinkedListInsert(BeforePos, " movlw high " + SourceArray)
              BeforePos = LinkedListInsert(BeforePos, " movwf " + ArrayHandler + "_H")
            End If
          ElseIf ModeAVR Then
            If SourceArrayPtr->Pointer = "POINTER" Then
              BeforePos = LinkedListInsert(BeforePos, " lds SysValueCopy," + SourceArrayHandler)
              BeforePos = LinkedListInsert(BeforePos, " sts " + ArrayHandler + ",SysValueCopy")
              BeforePos = LinkedListInsert(BeforePos, " lds SysValueCopy," + SourceArrayHandler + "_H")
              BeforePos = LinkedListInsert(BeforePos, " sts " + ArrayHandler + "_H,SysValueCopy")
            Else
              BeforePos = LinkedListInsert(BeforePos, " ldi SysValueCopy,low(" + SourceFunction + ")")
              BeforePos = LinkedListInsert(BeforePos, " sts " + ArrayHandler + ",SysValueCopy")
              BeforePos = LinkedListInsert(BeforePos, " ldi SysValueCopy,high(" + SourceArray + ")")
              BeforePos = LinkedListInsert(BeforePos, " sts " + ArrayHandler + "_H,SysValueCopy")
            End If
            AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
          End If
        END If

        CompileNextParam:
      Next

      'Mark Sub as required
      .Called->Required = -1
      'Mark file Sub came from as used
      SourceFile(.Called->SourceFile).InitSubUsed = -1

    End If

  End With

  'Copy code to output array
  CurrPos = OutList
  CurrPos = LinkedListInsertList(CurrPos, BeforeCode)
  CurrPos = LinkedListInsert(CurrPos, CallCmd)
  CurrPos = LinkedListInsertList(CurrPos, AfterCode)

  Return OutList
  End Function

Sub CompileSubCalls(CompSub As SubType Pointer)

  Dim As String TempLine, LineFromFile, BeforeFn, FunctionName, FunctionParams, AfterFn
  Dim As String FunctionType, Origin, NewOrigin, TempVarName, TempData
  Dim As String ReturnVar
  Dim As Integer CD, DS, S, E, BL, FB, F, PD, FoundFunction, MatchScore, BetterMatch
  Dim As Integer L, D, SL, Temp, UseTempVar, FunctionTypeID, CurrSub
  Dim As Integer ParamsInBrackets, CurrAliasByte, FirstBracketLoc
  Dim As Integer LastBracketLoc, BracketsRequired

  Dim As LinkedListElement Pointer CurrLine, NewCallCode, NewCallLine, LineBeforeCall
  Dim As LinkedListElement Pointer LineElements, CurrElement, MatchingSubs, MatchingSub
  Dim As LinkedListElement Pointer FindMatch

  Dim As SubCallType NewSubCall

  Dim TempVarCount(10) As Integer

  If CompSub->Name = "Main" or  Left(CompSub->Origin,5) = ";?F1L" Then
      MainSBC = MainSBC + 1
  End if


  'Prepare hash map of subs
  UpdateSubMap

  If (( compilerdebug and cCOMPILESUB ) = cCOMPILESUB )  Then
    Print "128 CompileSubCalls :  Call UpdateSubMap successful"
  End if

  'Find functions
  CurrLine = CompSub->CodeStart->Next
  Origin = ""
  Do While CurrLine <> 0

    'Check if startup Sub is needed for the line because of a constant
    DO WHILE INSTR(UCase(CurrLine->Value), ";STARTUP") <> 0
      Temp = VAL(Mid(CurrLine->Value, INSTR(UCase(CurrLine->Value), ";STARTUP") + 8))
      TempData = ";STARTUP" + Str(Temp)
      Replace CurrLine->Value, TempData, ""
      SourceFile(Temp).InitSubUsed = -1
      RequestSub(0, SourceFile(Temp).InitSub)
    Loop

    For PD = 1 to 10
      TempVarCount(PD) = 0
    Next

    FindFunctionsAgain:
    TempLine = CurrLine->Value

    NewOrigin = ""
    IF INSTR(TempLine, ";?F") <> 0 Then
      NewOrigin = MID(TempLine, INSTR(TempLine, ";?F"))
      If NewOrigin <> "" Then
        Origin = NewOrigin
        LastOrigin = Origin
      End If
    End If

    'Don't check "ON " statements for functions
    If UCASE(Left(TempLine, 3)) = "ON " Then Goto NextLineFunctions

    If (( compilerdebug and cCOMPILESUB ) = cCOMPILESUB )  and ( INSTR(UCase(TempLine), "PRESERVE") = 0 AND INSTR(UCase(TempLine), "REPROCES") = 0 ) Then
      Print "128 CompileSubCalls :  Process " + (TempLine)
    End if

    'Split up line
    LineElements = GetElements(TempLine)

    If (( compilerdebug and cCOMPILESUB ) = cCOMPILESUB )  and ( INSTR(UCase(TempLine), "PRESERVE") = 0 AND INSTR(UCase(TempLine), "REPROCES") = 0 ) Then
      Print "128 CompileSubCalls :       GetElements successful "
    End if

    'Run through list of functions
    CurrElement = LineElements->Next
    Do While CurrElement <> 0

      MatchingSubs = HashMapGet(Subroutines, UCase(CurrElement->Value))
      MatchingSub = MatchingSubs
      Do While MatchingSub <> 0

        If (( compilerdebug and cCOMPILESUB ) = cCOMPILESUB ) Then
          Print "128 CompileSubCalls :       Found matching Sub"
        End if

        'Line contains call to this subroutine
        CurrSub = MatchingSub->NumVal

        FunctionName = Subroutine(CurrSub)->Name
        FunctionType = Subroutine(CurrSub)->ReturnType
        FunctionTypeID = CastOrder(FunctionType)

        'Check to see if a line contains a function
        If INSTR(UCASE(TempLine), UCase(FunctionName)) = 0 THEN Goto CheckForNextFunction

        SearchLineAgain:
        FoundFunction = WholeINSTR(TempLine, FunctionName, 0)

        'Avoid calling functions from themselves
        'Note: ignores overloaded Sub and Function with same name, need to detect that later
        If FoundFunction = 2 And Subroutine(CurrSub)->IsFunction Then
          If CompSub->Name = Subroutine(CurrSub)->Name Then FoundFunction = 0
        END IF

        'Have already dealt with line
        If NewOrigin = "" And Left(TempLine, 6) = " call " Then
          FoundFunction = 0
        End If

        IF FoundFunction <> 2 And INSTR(UCase(TempLine), UCase(FunctionName)) <> 0 THEN
          Replace TempLine, FunctionName, CHR(30) + STR(CurrSub) + CHR(30)
          Goto SearchLineAgain
        END IF

        'If it does, call a sub, and get the value after
        IF FoundFunction = 2 THEN
          'Print "Found function: "; FunctionName

          If (( compilerdebug and cCOMPILESUB ) = cCOMPILESUB ) Then
            Print "128 CompileSubCalls :       Found function: " + FunctionName 
          End if

          'Check if a temp variable should be used
          UseTempVar = 0
          If Subroutine(CurrSub)->IsFunction Then
            If CountOccur(TempLine, FunctionName, -1) > 1 Then
              UseTempVar = -1
              TempVarCount(FunctionTypeID) += 1
              TempVarName = "SysFn" + FunctionType + Str(TempVarCount(FunctionTypeID))
              AddVar TempVarName, FunctionType, 1, CompSub, "REAL", Origin, , -1
            End If
            'Add a variable for the Function result
            AddVar Subroutine(CurrSub)->Name, FunctionType, 1, Subroutine(CurrSub), "REAL", Origin, , -1
            AddVar Subroutine(CurrSub)->Name, FunctionType, 1, CompSub, "REAL", Origin, , -1
          End If

          'If line starts with call or GOSUB, remove call
          If Left(TempLine, 5) = "CALL " Then
            TempLine = Trim(Mid(TempLine, 6))
          ElseIf Left(TempLine, 6) = "GOSUB " Then
            TempLine = Trim(Mid(TempLine, 7))
          End If

          'Replace the Function in the line
          'Get whatever is to the left of the function
          BeforeFn = LEFT(TempLine, INSTR(UCase(TempLine), FunctionName) - 1)

          'If there's something before, make sure we actually have a function
          If BeforeFn <> "" And Not Subroutine(CurrSub)->IsFunction Then
            Replace TempLine, FunctionName, CHR(30) + STR(CurrSub) + CHR(30)
            Goto SearchLineAgain
          End If

          'Get the parameters of the Function and the text after it
          FunctionParams = ""

          AfterFn = MID(TempLine, INSTR(UCase(TempLine), UCase(FunctionName)) + LEN(FunctionName))

          'Check to see if parameters are in brackets
          'FunctionName(params) - yes
          'SubName(param) - yes
          'SubName param - no
          'SubName (param1 + x), param2 - no
          'FunctionName(param) ms - yes
          'So, check for comma inside or outside brackets

          'Are brackets required?
          BracketsRequired = BeforeFn <> ""
          'Parse parameters
          BL = 0
          FirstBracketLoc = -1
          LastBracketLoc = -1
          For FB = 1 To Len(AfterFn)
            Select Case Mid(AfterFn, FB, 1)
              Case "("
                'Note position of opening bracket
                BL += 1
                If BL = 1 And FirstBracketLoc = -1 Then
                  FirstBracketLoc = FB
                End If
              Case ")"
                'Note position of closing bracket
                BL -= 1
                If BL = 0 Then
                  'If match found and all parameters must be in brackets, this is the end of these parameters
                  If BracketsRequired Then
                    LastBracketLoc = FB
                    Exit For
                  End If
                  'If we haven't seen a comma yet, this could be the end of the parameters
                  If LastBracketLoc <> -2 Then
                    LastBracketLoc = FB
                  End If
                End If
              Case " "
                'Do nothing
              Case ","
                'If a comma is found outside of brackets, the brackets do not mark the parameters
                If BL = 0 Then
                  LastBracketLoc = -2
                End If
              Case Else
                'If any other character is found before brackets, these brackets do not mark parameters
                'Example: SomeSub SomeFunction(param)
                If BL = 0 And FirstBracketLoc = -1 Then
                  LastBracketLoc = -2
                End If

                'If a mathematical operator is found after brackets, brackets do not mark parameters
                If BL = 0 And IsCalcDivider(Mid(AfterFn, FB, 1)) Then
                  LastBracketLoc = -2
                End If
            End Select
          Next
          'Extract parameters from brackets if brackets used
          If FirstBracketLoc <> -1 And LastBracketLoc > 0 Then
            FunctionParams = Trim(Mid(AfterFn, FirstBracketLoc + 1, LastBracketLoc - FirstBracketLoc - 1))
            
            'For a subroutine, remove anything after parameters
            If Subroutine(CurrSub)->IsFunction Then
              AfterFn = Mid(AfterFn, LastBracketLoc + 1)
            Else
              AfterFn = ""
            End If
          Else
            'No brackets - nothing or everything after sub/Function name is a parameter
            If BracketsRequired Then
              'If we have a Function without brackets, no parameters
              FunctionParams = ""
            Else
              'If we have a Sub without brackets, everything after the name is a parameter
              FunctionParams = Trim(AfterFn)
              AfterFn = ""
            End If
          End If

          If (( compilerdebug and cCOMPILESUB ) = cCOMPILESUB ) Then
            Print "128 CompileSubCalls :       FunctionParams: " + FunctionParams 
          End if

          ValidateParameterIsValid( Subroutine(CurrSub)->Name, FunctionParams, Origin )

          If (( compilerdebug and cCOMPILESUB ) = cCOMPILESUB ) Then
            Print "128 CompileSubCalls :       FunctionParams: Validated" 
          End if

          'Remove origin from FunctionParams
          IF INSTR(FunctionParams, ";?F") <> 0 Then
            FunctionParams = RTrim(Left(FunctionParams, InStr(FunctionParams, ";?F") - 1))
          End If

          'Detect cases where overloaded Sub and Function have same name, and return of Function is set
          'Need to detect here or the Sub will be called with = return value as a parameter.
          If Subroutine(CurrSub)->Overloaded And Left(FunctionParams, 1) = "=" Then
            Replace TempLine, FunctionName, CHR(30) + STR(CurrSub) + CHR(30)
            Goto SearchLineAgain
          End If

          If (( compilerdebug and cCOMPILESUB ) = cCOMPILESUB ) Then
            Print "128 CompileSubCalls :       Extracting Parameters" 
          End if

          'Prepare Sub call
          'Print "Calling:"; FunctionName, "Params:"; FunctionParams
          ExtractParameters(NewSubCall, FunctionName, FunctionParams, Origin)
        
          If (( compilerdebug and cCOMPILESUB ) = cCOMPILESUB ) Then
            Print "128 CompileSubCalls :       ExtractParameters: Successful" 
          End if

          With NewSubCall
            .Called = Subroutine(CurrSub)
            .Caller = CompSub
            .CalledID = CurrSub
            .Origin = Origin
          End With

          'Check Function being called matches current Function best
          If Subroutine(CurrSub)->Overloaded Then
            MatchScore = SubSigMatch(GetSubSig(*Subroutine(CurrSub)), NewSubCall.CallSig)
            BetterMatch = 0
            'Check for better matches
            FindMatch = MatchingSub->Next
            Do While FindMatch <> 0
              If Subroutine(CurrSub)->Name = Subroutine(FindMatch->NumVal)->Name Then
                If SubSigMatch(GetSubSig(*Subroutine(FindMatch->NumVal)), NewSubCall.CallSig) > MatchScore Then
                  BetterMatch = -1
                  Exit Do
                End If
              End If

              FindMatch = FindMatch->Next
            Loop

            'Skip this Sub if it's not the best
            If BetterMatch Then
              Replace TempLine, FunctionName, CHR(30) + STR(CurrSub) + CHR(30)
              Goto SearchLineAgain
            End If
          End If

          'Get return variable
          ReturnVar = ""
          If NewSubCall.Called->IsFunction Then
            If NewSubCall.Called->Overloaded Then
              If UCase(NewSubCall.Called->ReturnType) = "STRING" Then
                Dim Temp as String
                Temp = Message("OverLoadFunctionsNotSupported")
                Replace ( Temp, "%functionname%", NewSubCall.Called->Name)
                LogError Temp, Origin
                WriteErrorLog
                End

            Else
                ReturnVar = "SYS" + Chr(31) + Str(CurrSub) + CHR(31) + UCase(NewSubCall.Called->ReturnType)

                'Add alias for return variable with appropriate type
                If NewSubCall.Called->ReturnAlias = "" Then
                  For CurrAliasByte = GetTypeSize(NewSubCall.Called->ReturnType) - 1 To 0 Step -1
                    If NewSubCall.Called->ReturnAlias = "" Then
                      NewSubCall.Called->ReturnAlias = GetByte(NewSubCall.Called->Name, CurrAliasByte)
                    Else
                      NewSubCall.Called->ReturnAlias += "," + GetByte(NewSubCall.Called->Name, CurrAliasByte)
                    End If
                  Next
                  AddVar("SYS" + UCase(NewSubCall.Called->Name + NewSubCall.Called->ReturnType), NewSubCall.Called->ReturnType, 1, 0, "ALIAS:" + NewSubCall.Called->ReturnAlias, Origin,, -1)
                End If
              End If
            Else
              ReturnVar = CHR(31) + Str(CurrSub) + CHR(31)
            End If
          End If

          'Adjust line
          'Use 31 where name needs removal to prevent trying to handle it twice
          If UseTempVar Then
            TempLine = BeforeFn + TempVarName + AfterFn
          Else
            TempLine = BeforeFn + ReturnVar + AfterFn
          End If

          'Write back code
          'Print DS, BeforeFn, FunctionName, FunctionParams, AfterFn
          If (( compilerdebug and cCOMPILESUB ) = cCOMPILESUB ) Then
            Print "128 CompileSubCalls :       Write back: ";
            Print BeforeFn, FunctionName, FunctionParams, AfterFn 
          End if

          If Subroutine(CurrSub)->IsFunction Then

            'If vars are empty we can assume the Function as no assignment.
            If Len(Trim(BeforeFn)) = 0 and Len(Trim(AfterFn)) = 0 and Len(Trim(FunctionParams)) = 0 Then
              Dim Temp as String
              Temp = Message("MissingFunctionAssignment")
              LogError Temp, Origin
            End If

            If NewSubCall.Called->Overloaded Then
              CurrLine->Value = ";FNSTART," + CHR(31) + Str(CurrSub) + CHR(31) + Str(CurrSub)
            Else
              CurrLine->Value = ";FNSTART," + CHR(31) + Str(CurrSub) + CHR(31)
            End If
          Else
            CurrLine = LinkedListDelete(CurrLine)
          End If
          LineBeforeCall = CurrLine
          CurrLine = LinkedListInsertList(CurrLine, CompileSubCall(@NewSubCall))

          'Record call
          If Not NewSubCall.Called->IsMacro Then
            RecordSubCall(CompSub, NewSubCall.Called)
          End If

          If TempLine <> "" Then
            If UseTempVar Then
              CurrLine = LinkedListInsert(CurrLine, TempVarName + "=" + ReturnVar)
              LinkedListInsert(CurrLine, TempLine)
            Else
              LinkedListInsert(CurrLine, TempLine)
            End If
          End If

          'Need to check line again, in case of nested functions
          CurrLine = LineBeforeCall
          GoTo NextLineFunctions

        END If

        CheckForNextFunction:
        'Use 30 for temporarily removing name, ie when close match occurs
        Dim OldName As String
        Do While Instr(TempLine, Chr(30)) <> 0
          OldName = MID(TempLine, Instr(TempLine, Chr(30)) + 1)
          OldName = Left(OldName, INSTR(OldName, Chr(30)) - 1)
          Replace TempLine, Chr(30) + OldName + Chr(30), Subroutine(Val(OldName))->Name
        Loop

        MatchingSub = MatchingSub->Next
      Loop

      CurrElement = CurrElement->Next
    Loop

    NextLineFunctions:
    CurrLine = CurrLine->Next
  Loop

  'Deal with any missed names
  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0

    'Delete any lines with just a Sub name
    If Left(CurrLine->Value, 1) = Chr(31) And InStr(CurrLine->Value, "=") = 0 Then
      CurrLine = LinkedListDelete(CurrLine)
    Else
      'Replace tokens with names
      CurrLine->Value = ReplaceFnNames(CurrLine->Value)
    End If
    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub CompileTables
  Dim As String Temp, Source, Table, ThisItem
  Dim As Integer PD, SP, CurrTableByte, StringTablesUsed, LargeTable
  Dim As Integer LastNonZeroElement, CurrElement

  Dim As LinkedListElement Pointer CurrLine
  Dim As SubType Pointer TableSub

  'Add string lookup tables
  StringTablesUsed = 0
  For PD = 1 To SSC
    If StringStore(PD).Used Then
      StringTablesUsed = -1
      Exit For
    End If
  Next

  IF StringTablesUsed THEN
    IF VBS = 1 THEN PRINT : PRINT SPC(5); Message("StringTable")

    'Create Sub for string tables
    TableSub = NewSubroutine("SysStringTables")
    SBC += 1: Subroutine(SBC) = TableSub
    CurrLine = TableSub->CodeStart
    TableSub->Compiled = -1
    TableSub->Required = 0
    TableSub->NoReturn = -1

    AddVar "StringPointer", "BYTE", 1, Subroutine(0), "REAL", "", , -1
    AddVar "SysStringA", "WORD", 1, Subroutine(0), "REAL", "", , -1

    If ModePIC And ChipFamily <> 16 Then
      If ChipFamily = 12 Then
        CurrLine = LinkedListInsert(CurrLine, " movf SysStringA,W")
        CurrLine = LinkedListInsert(CurrLine, " incf SysStringA,F")
        CurrLine = LinkedListInsert(CurrLine, " movwf PCL")
        LogWarning Message("WarningUnsafeString")
      Else
        'Get current pointer location
        CurrLine = LinkedListInsert(CurrLine, " movf SysStringA_H,W")
        CurrLine = LinkedListInsert(CurrLine, " movwf PCLATH")
        CurrLine = LinkedListInsert(CurrLine, " movf SysStringA,W")
        'Code will increment pointer for next access
        CurrLine = LinkedListInsert(CurrLine, " incf SysStringA,F")
        CurrLine = LinkedListInsert(CurrLine, " btfsc STATUS,Z")
        CurrLine = LinkedListInsert(CurrLine, " incf SysStringA_H,F")
        'Alter PCL
        CurrLine = LinkedListInsert(CurrLine, " movwf PCL")
      End If

    End If

    For PD = 1 TO SSC
      Table = "StringTable" + Str(PD) + LabelEnd
      Source = StringStore(PD).Value
      If StringStore(PD).Used Then
        CurrLine = LinkedListInsert(CurrLine, "")
        CurrLine = LinkedListInsert(CurrLine, Table)
        GetMetaData(Currline)->IsLabel = -1
        If ModePIC Then
          'Table on low/mid range chip, use retlw
          If ChipFamily <> 16 THEN
              CurrLine = LinkedListInsert(CurrLine, " retlw " + Str(LEN(Source)))
            For SP = 1 TO LEN(Source)
              Temp = " retlw " + Str(ASC(Mid(Source, SP, 1)))
              IF Mid(Source, SP, 1) <> "=" THEN Temp = Temp + Chr(9) + ";" + Mid(Source, SP, 1)
              IF Mid(Source, SP, 1) = "=" THEN Temp = Temp + Chr(9) + "; (equals)"
              CurrLine = LinkedListInsert(CurrLine, Temp)
            Next
          'Table on high end, can use db and then read with tblrd
          Else
            Temp = ""
            FOR SP = 0 TO LEN(Source)
              If SP = 0 Then
                ThisItem = Str(Len(Source))
              Else
                ThisItem = Str(Asc(Mid(Source, SP, 1)))
              End If
              If Len(Temp) + Len(ThisItem) >= 79 And Temp <> "" And (SP Mod 2) = 0 Then
                CurrLine = LinkedListInsert(CurrLine, " db " + Temp)
                Temp = ThisItem
              Else
                If Temp = "" Then
                  Temp = ThisItem
                Else
                  Temp = Temp + "," + ThisItem
                End If
              End If
            NEXT
            If Temp <> "" Then CurrLine = LinkedListInsert(CurrLine, " db " + Temp)
          End If

        ElseIf ModeAVR Then
          Temp = Str(LEN(Source))
          For SP = 1 TO LEN(Source)
            Temp = Temp + "," + Str(ASC(Mid(Source, SP, 1)))
          Next
          'Need to pad with 0 to make even length
          If Len(Source) Mod 2 = 0 Then
            Temp = Temp + ",0"
          End If
          CurrLine = LinkedListInsert(CurrLine, ".DB " + Temp)
        End If

        TableSub->Required = -1
        CurrLine = LinkedListInsert(CurrLine, "")

        IF AFISupport = 1  and ModePIC and ChipFamily = 16 Then
          CurrLine = LinkedListInsert(CurrLine, " ALIGN 2;X3")
        End if

      End If

    Next


  End If

  'Add data tables (program memory)
  IF DataTables > 0 Then
    If VBS = 1 THEN PRINT : PRINT SPC(5); Message("DataTable")
    For PD = 1 TO DataTables

      'If no data memory, move all tables to program memory
      If DataTable(PD).Used And DataTable(PD).StoreLoc = 1 And ChipEEPROM = 0 Then
        LogWarning Message("WarningNoEEPROMTable")
        DataTable(PD).StoreLoc = 0
      End If

      If DataTable(PD).Used And DataTable(PD).StoreLoc = 0 THEN
        If VBS = 1 Then Print Spc(10); DataTable(PD).Name
        DataTable(PD).Item(0) = DataTable(PD).Items

        'Is this a large data table?
        LargeTable = 0
        If DataTable(PD).Items > 255 Then
          LargeTable = -1
        End If

        'Create Sub for data tables
        'Need to create a table for each byte in data
        For CurrTableByte = 0 To GetTypeSize(DataTable(PD).Type) - 1
          Table = GetByte("TABLE" + DataTable(PD).Name, CurrTableByte)   'was capitalised
          TableSub = NewSubroutine(GetByte(DataTable(PD).Name, CurrTableByte))

          SBC += 1: Subroutine(SBC) = TableSub
          CurrLine = TableSub->CodeStart




          TableSub->Compiled = -1
          TableSub->Required = -1
          TableSub->NoReturn = -1

          'Find last element in table that is non-zero
          LastNonZeroElement = -1
          For CurrElement = DataTable(PD).Items To 0 Step -1
            If ((DataTable(PD).Item(CurrElement) Shr CurrTableByte * 8) And 255) <> 0 Then
              LastNonZeroElement = CurrElement
              Exit For
            End If
          Next

          If ModePIC Then
            If LargeTable Then
              AddVar "SysStringA", "WORD", 1, Subroutine(0), "REAL", "", , -1
            Else
              AddVar "SysStringA", "BYTE", 1, Subroutine(0), "REAL", "", , -1
            End If

            If ChipFamily = 12 THEN
              CurrLine = LinkedListInsert(CurrLine, " movf SysStringA, W")
              CurrLine = LinkedListInsert(CurrLine, " addwf PCL, F")

            ElseIf ChipFamily = 14 Or ChipFamily = 15 Then

              'Check item number, return 0 if number is too big
              If LargeTable Then
                CurrLine = LinkedListInsertList(CurrLine, CompileConditions("SYSSTRINGA<" + Str(LastNonZeroElement + 1), "FALSE", ""))
              Else
                CurrLine = LinkedListInsert(CurrLine, " movlw " + Str(LastNonZeroElement + 1))
                CurrLine = LinkedListInsert(CurrLine, " subwf SysStringA, W")
                CurrLine = LinkedListInsert(CurrLine, " btfsc STATUS, C")
              End If
              CurrLine = LinkedListInsert(CurrLine, " retlw 0")
              'Retrieve item
              If LastNonZeroElement >= 0 Then
                CurrLine = LinkedListInsert(CurrLine, " movf SysStringA, W")
                CurrLine = LinkedListInsert(CurrLine, " addlw low " + Table)
                CurrLine = LinkedListInsert(CurrLine, " movwf SysStringA")
                If ChipFamily = 15 Then
                  CurrLine = LinkedListInsert(CurrLine, " movlw (high " + Table + ") & 127")
                Else
                  CurrLine = LinkedListInsert(CurrLine, " movlw high " + Table)
                End If
                CurrLine = LinkedListInsert(CurrLine, " btfsc STATUS, C")
                CurrLine = LinkedListInsert(CurrLine, " addlw 1")
                If LargeTable Then
                  CurrLine = LinkedListInsert(CurrLine, " addwf SysStringA_H, W")
                End If
                CurrLine = LinkedListInsert(CurrLine, " movwf PCLATH")
                CurrLine = LinkedListInsert(CurrLine, " movf SysStringA, W")
                CurrLine = LinkedListInsert(CurrLine, " movwf PCL")
                CurrLine = LinkedListInsert(CurrLine, Table)
                GetMetaData(CurrLine)->IsLabel = -1
              End If

            ElseIf ChipFamily = 16 Then
              If LargeTable Then
                CurrLine = LinkedListInsertList(CurrLine, CompileConditions("SYSSTRINGA<" + Str(LastNonZeroElement + 1), "FALSE", ""))
              Else
                CurrLine = LinkedListInsert(CurrLine, " movlw " + Str(LastNonZeroElement + 1))
                CurrLine = LinkedListInsert(CurrLine, " cpfslt SysStringA")
              End If
              CurrLine = LinkedListInsert(CurrLine, " retlw 0")

              If LastNonZeroElement >= 0 Then
                CurrLine = LinkedListInsert(CurrLine, " movf SysStringA, W")
                CurrLine = LinkedListInsert(CurrLine, " addlw low " + Table)
                CurrLine = LinkedListInsert(CurrLine, " movwf TBLPTRL")
                CurrLine = LinkedListInsert(CurrLine, " movlw high " + Table)
                If LargeTable Then
                  CurrLine = LinkedListInsert(CurrLine, " addwfc SysStringA_H, W")
                Else
                  CurrLine = LinkedListInsert(CurrLine, " btfsc STATUS, C")
                  CurrLine = LinkedListInsert(CurrLine, " addlw 1")
                End If
                CurrLine = LinkedListInsert(CurrLine, " movwf TBLPTRH")
                If HighTBLPTRBytes > 2 Then
                  CurrLine = LinkedListInsert(CurrLine, " movlw upper " + Table)
                  CurrLine = LinkedListInsert(CurrLine, " btfsc STATUS, C")
                  CurrLine = LinkedListInsert(CurrLine, " addlw 1")
                  CurrLine = LinkedListInsert(CurrLine, " movwf TBLPTRU")
                End If
                CurrLine = LinkedListInsert(CurrLine, " tblrd*")
                CurrLine = LinkedListInsert(CurrLine, " movf TABLAT, W")
                CurrLine = LinkedListInsert(CurrLine, " return")
                CurrLine = LinkedListInsert(CurrLine, Table)
                GetMetaData(CurrLine)->IsLabel = -1
                Temp = ""
                FOR SP = 0 TO LastNonZeroElement
                  ThisItem = GetByte(Str(DataTable(PD).Item(SP)), CurrTableByte)
                  If Len(Temp) + Len(ThisItem) >= 79 And Temp <> "" And (SP Mod 2) = 0 Then
                    CurrLine = LinkedListInsert(CurrLine, " db " + Temp)
                    Temp = ThisItem
                  Else
                    If Temp = "" Then
                      Temp = ThisItem
                    Else
                      Temp = Temp + "," + ThisItem
                    End If
                  End If
                NEXT
                If Temp <> "" Then CurrLine = LinkedListInsert(CurrLine, " db " + Temp)
              Else
                CurrLine = LinkedListInsert(CurrLine, " retlw 0")
              End If
            END IF

            If ChipFamily <> 16 Then
              FOR SP = 0 TO LastNonZeroElement
                CurrLine = LinkedListInsert(CurrLine, " retlw " + GetByte(Str(DataTable(PD).Item(SP)), CurrTableByte))
              NEXT
            End If

            IF AFISupport = 1  and ModePIC and ChipFamily = 16 Then
              CurrLine = LinkedListInsert(CurrLine, " ALIGN 2;X4")
            End if

          ElseIf ModeAVR Then
            'Check requested location
            If LargeTable Then
              CurrLine = LinkedListInsertList(CurrLine, CompileConditions("SYSSTRINGA<" + Str(LastNonZeroElement + 1), "FALSE", ""))
              'If SysStringA < max size, then SysByteTempX = 255, and this instruction is skipped
              'If it's the same, then SysByteTempX = 0, and this instruction is executed
              CurrLine = LinkedListInsert(CurrLine, " ret")
            Else
              CurrLine = LinkedListInsert(CurrLine, " cpi SysStringA, " + Str(LastNonZeroElement + 1))
              CurrLine = LinkedListInsert(CurrLine, " brlo PC + 3")
              CurrLine = LinkedListInsert(CurrLine, " clr SysByteTempX")
              CurrLine = LinkedListInsert(CurrLine, " ret")
            End If

            '4/1/2010: added << 2 to ldi
            CurrLine = LinkedListInsert(CurrLine, " ldi SysReadA, low(" + Table + "<<1)")
            CurrLine = LinkedListInsert(CurrLine, " ldi SysReadA_H, high(" + Table + "<<1)")
            CurrLine = LinkedListInsert(CurrLine, " add SysReadA, SysStringA")
            If LargeTable Then
              CurrLine = LinkedListInsert(CurrLine, " adc SysReadA_H, SysStringA_H")
            Else
              CurrLine = LinkedListInsert(CurrLine, " brcc PC + 2")
              CurrLine = LinkedListInsert(CurrLine, " inc SysReadA_H")
            End If

            If ChipFamily = 121 then
              CurrLine = LinkedListInsert(CurrLine, " sbr  SysReadA_H,1<<6") 'added 0x4000 to address PROGMEM by setting the one bit
              CurrLine = LinkedListInsert(CurrLine, " ld sysbytetempx, z") 'Value will be read, stored in R16
            Else
              CurrLine = LinkedListInsert(CurrLine, " lpm") 'Value will be read, stored in R0

            End if
            CurrLine = LinkedListInsert(CurrLine, " ret")

            CurrLine = LinkedListInsert(CurrLine, Table + ":")
            Temp = ""

            FOR SP = 0 TO LastNonZeroElement
              ThisItem = GetByte(Str(DataTable(PD).Item(SP)), CurrTableByte)
              If Len(Temp) + Len(ThisItem) >= 79 And Temp <> "" And (SP Mod 2) = 0 Then
                CurrLine = LinkedListInsert(CurrLine, " .DB " + Temp)
                Temp = ThisItem
              Else
                If Temp = "" Then
                  Temp = ThisItem
                Else
                  Temp = Temp + "," + ThisItem
                End If
              End If
            NEXT
            If Temp <> "" Then CurrLine = LinkedListInsert(CurrLine, " .DB " + Temp)

            If LargeTable Then
              AddVar "SysStringA", "WORD", 1, 0, "REAL", "", , -1
            Else
              AddVar "SysStringA", "BYTE", 1, 0, "REAL", "", , -1
            End If
            AddVar "SysReadA", "WORD", 1, 0, "REAL", "", , -1
          End If

        Next
      END If

    NEXT
  End IF
  
  End Sub

Function CompileVarSet (SourceIn As String, Dest As String, Origin As String, InvertBitCopy As Integer = 0) As LinkedListElement Pointer

  Dim As String SType, DType, Temp, DestTemp, SourceTemp, CSource, SCastType
  Dim As String LTemp, HTemp, UTemp, ETemp, STemp, Source, ReferencedSub
  Dim As Integer CurrVarByte, LastConst, ThisConst, DestIsDouble
  Dim As Integer DestReg, DestIO, SourceReg, SourceIO, L, H, U, E, CD, DestIOAVRDx
  Dim As Integer RequiresGlitchFree, DestVarBitNo, UseIndirectBitSet
  Dim As LongInt S
  Dim As PinDirType Pointer CurrPinDir
  Dim As SysVarType Pointer SysVarBit
  Dim As SubType Pointer CurrentSub, DestSub

  Dim As LinkedListElement Pointer CurrLine, OutList
  OutList = LinkedListCreate
  CurrLine = OutList

  'Check parameters
  if Len( SourceIn ) = 0  then
      LogWarning( Message("NoSourceParam"), Origin)
  End if
  if Len( Dest ) = 0 then
      LogWarning( Message("NoDestParam"), Origin)
  End if

  'Initialise
  Source = SourceIn
  CurrentSub = Subroutine(GetSubID(Origin))
  IF INSTR(Origin, "D") <> 0 Then DestSub = Subroutine(GetDestSub(Origin)) Else DestSub = CurrentSub

  'Get types
  If Instr(Source, "[") <> 0 And InStr(Source, "]") <> 0 Then
    'Remove cast from source name
    'Show warning if it's not the first thing
    If Left(Source, 1) <> "[" Then
      LogWarning(Message("WarningCastBadPlace"), Origin)
    End If
    SCastType = Mid(Source, InStr(Source, "[") + 1)
    Source = Left(Source, InStr(Source, "[") - 1)
    Source = Source + Mid(SCastType, InStr(SCastType, "]") + 1)
    SCastType = UCase(Left(SCastType, InStr(SCastType, "]") - 1))
    If IsConst(Source) Then
      SType = "CONST"

    Else
      'Do not allow cast to increase source type - this causes problems
      SType = TypeOfVar(Source, CurrentSub)
      If CastOrder(SCastType) <= CastOrder(SType) Then
        SType = SCastType
      End If
    End If

  Else



    SType = TypeOfVar(Source, CurrentSub)
    If IsConst(Source) Then SType = "CONST"
    IF INSTR(Source, ";STRING") <> 0 THEN
      If Len(GetString(Source)) = 1 Then
        SType = "CONST"
        Source = Str(ASC(GetString(Source)))
      Else
        SType = "SCONST"
      End If
    End If
  End If

  If Instr(Dest, "[") <> 0 And InStr(Dest, "]") <> 0 Then
    'Remove cast from source name
    'Show warning if it's not the first thing
    If Left(Dest, 1) <> "[" Then
      LogWarning(Message("WarningCastBadPlace"), Origin)
    End If
    DType = Mid(Dest, InStr(Dest, "[") + 1)
    Dest = Left(Dest, InStr(Dest, "[") - 1)
    Dest = Dest + Mid(DType, InStr(DType, "]") + 1)
    DType = UCase(Left(DType, InStr(DType, "]") - 1))
    If IsConst(Dest) Then SType = "CONST"

  Else
    DType = TypeOfVar(Dest, DestSub)
  End If

  'Don't copy var to itself
  If Source = Dest Then
    Return OutList
  End If

  If ChipAVRDX Then
    If IsIORegDX(Dest) Then
    ' print dest, GetSysVarAliasName ( Dest ), IsIORegDX(Dest)
      'Map legacy megaAVR register to DX register using the Alias Addressing
      If (( compilerdebug and cAVRDXDEBUG ) = cAVRDXDEBUG )  Then
        CurrLine = LinkedListInsert(CurrLine, ";IOAVRDX #5 Dest: " + Dest)
      End if
      Dest = GetSysVarAliasName ( Dest ) 
    End If   

    If IsIORegDX(Source) Then
      'Map legacy megaAVR register to DX register using the Alias Addressing
      If (( compilerdebug and cAVRDXDEBUG ) = cAVRDXDEBUG )  Then
        CurrLine = LinkedListInsert(CurrLine, ";IOAVRDX #6 Source: " + Source)
      End if
      Source = GetSysVarAliasName ( Source ) 
    End If   
  End If

  DestIOAVRDx = IsIORegDX(Dest)
  DestReg = IsRegister(Dest) or IsIORegDX(Dest)
  DestIO = IsIOReg(Dest)
  SourceReg = IsRegister(Source)
  SourceIO = IsIOReg(Source)

  ' Correct numeric defintions
  If Stype = "CONST" and ( Dtype = "SINGLE" or Dtype = "DOUBLE" ) Then

    If CountOccur( Source, ".") > 1 Then
      Temp = Message("BadValueType")
      Replace Temp, "%value%", Source
      Replace Temp, "%type%", LCase(DType)
      Replace Temp, "%var%", Dest
      LogError Temp, Origin
    End If

    If Val(Trim(Source)) = 0 Then 
      Source = "0.0"
    ElseIf Right(Trim(Source),1) = "." Then
      'Always add 0 to nn. to give nn.0
      Source = Source + "0"  
    Else 
      If Left(Trim(Source),1) = "." Then
        'Always add 0 to . to give 0.nnn
        Source = "0" + Source
      End if

      'All add .0 when nn to give nn.0
      If Instr(Source,".") = 0 Then 
        If Trim(Source) = Trim(Str(VAL(Source))) Then
          Source = Source + ".0"
        End If
      End if
    End If

    'Remove Leading 0
    If Left(Source,1) = "0" Then
      Do While Left(Source,1) = "0" And Mid( Source, 2, 1) <> "."
        Source = Mid(Source, 2)
      Loop 
    End If

    'Remove trailing 0
    If Right(Source,1) = "0" Then
      Do While Right(Source,1) = "0" And Mid( Source, Len(Source)-1, 1) <> "."
        Source = Mid(Source, 1, Len(Source) - 1)
      Loop
    End If

  ElseIf SType = "BIT" and ( Dtype = "SINGLE" or Dtype = "DOUBLE" ) Then
      'check for double decimal point
      If CountOccur( Source, ".") > 1 Then
        Temp = Message("BadValueType")
        Replace Temp, "%value%", Source
        Replace Temp, "%type%", LCase(DType)
        Replace Temp, "%var%", Dest
        LogError Temp, Origin
      End If
  End if 

  If (( compilerdebug and cVAR_SET ) = cVAR_SET ) Then
    Print " 2 VAR SET       : " + Dest + "[" + DType + "] = " + Source + "[" + SType + "]"
  End If

  'Record reads and writes (for auto pin direction setting)
  If DType = "BIT" Or DType = "BYTE" Then
    'If writing to a pin/port, record it
    'Unless setting port to 0 - this is done by InitSys and should not be recorded
    CurrPinDir = GetPinDirection(Dest)
    If CurrPinDir <> 0 Then
      If Not IsConst(Source) Or MakeDec(Source) <> 0 Or DType = "BIT" Then
        CurrPinDir->WrittenTo = -1
      End If
    End If
  End If
  If SType = "BIT" Or SType = "BYTE" Then
    'If reading from a pin/port, record it
    CurrPinDir = GetPinDirection(Source)
    If CurrPinDir <> 0 Then
      CurrPinDir->ReadFrom = -1
    End If
  End If

  'Record references to subroutines (for indirect calling)
  If Left(Source, 1) = "@" Then
    'Print "Reference found:" + Source
    ReferencedSub = Mid(Source, 2)
    RequestSub(0, ReferencedSub, "")
  End If

  'Generate copy code
  'Copies between integers of same or different size
  If IsIntType(SType) And IsIntType(DType) Then
    'Print "New: "; Dest; " = "; Source
    'Don't add variables here, will result in duplicate definitions
    '(if setting VAR_H here, then VAR is defined as word later)
    'If DType = "BYTE" Then AddVar Dest, "BYTE", 1, 0, "REAL", Origin
    'If SType = "BYTE" Then AddVar Source, "BYTE", 1, 0, "REAL", Origin

    'Need to check if big constant is getting put into a small variable
    If SType = "CONST" And Not AllowOverflow Then

      ' Check that a decimal point is only assigned to double or single
      If InStr(Source, ".") <> 0 and ( UCase(DType) <> "DOUBLE" or UCase(DType) <> "SINGLE" )Then
          Temp = Message("BadValueType")
          Replace Temp, "%value%", Source
          Replace Temp, "%type%", LCase(DType)
          Replace Temp, "%var%", Dest
          LogError Temp, Origin
          Source = "0"
      End if

      If InStr(Source, "@") = 0 Then
        S = MakeDec(Source)
        If Not IsValidValue(S, DType) Then
          Temp = Message("BadValueType")
          Replace Temp, "%value%", Source
          Replace Temp, "%type%", LCase(DType)
          Replace Temp, "%var%", Dest
          LogError Temp, Origin
          Source = "0"
        End If
      End If
    End If

    'Remember last value loaded in to W/SysValueCopy
    '-1 = nothing loaded, or unknown
    LastConst = -1

    For CurrVarByte = 0 To GetTypeSize(DType) - 1

      'Copy each byte
      'Source is a constant, or source is too small
      If SType = "CONST" Or GetTypeSize(SType) <= CurrVarByte Then
        'For integer:
        'MakeTwosComp
        'Get value to load in to current byte
        If SType = "CONST" Then
          If Instr(Source, "@") <> 0 Then
            S = 1
            'Is "high" or "low" already present?
            If Left(Source, 1) <> "@" Then
              STemp = Source
              Replace STemp, "@", ""
            Else
              STemp = GetByte(Source, CurrVarByte)
            End If
            ThisConst = -1
          Else
            STemp = GetByte(Source, CurrVarByte)
            S = Val(STemp)
            ThisConst = S
          End If
        Else
          'Trying to load high byte of smaller variable
          S = 0
          STemp = "0"
          ThisConst = 0
        End If

        'Set byte
        If ModePIC Then
          If ChipFamily = 16 And S = 255 Then
            CurrLine = LinkedListInsert(CurrLine, " setf " + GetByte(Dest, CurrVarByte))
          ElseIf S > 0 Then
            If LastConst = -1 Or LastConst <> ThisConst Then
              CurrLine = LinkedListInsert(CurrLine, " movlw " + STemp)
              LastConst = ThisConst
            End If
            CurrLine = LinkedListInsert(CurrLine, " movwf " + GetByte(Dest, CurrVarByte))
          Else
            CurrLine = LinkedListInsert(CurrLine, " clrf " + GetByte(Dest, CurrVarByte))
          End If
        ElseIf ModeAVR Then
          If DestReg Then
            AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
            CurrLine = LinkedListInsert(CurrLine, " ldi SysValueCopy," + STemp)
            If DestIOAVRDx Then
              If (( compilerdebug and cAVRDXDEBUG ) = cAVRDXDEBUG )  Then
                  CurrLine = LinkedListInsert(CurrLine, ";IOAVRDX #1")
              End If  
              CurrLine = LinkedListInsert(CurrLine, " sts " + GetByte(GetSysVarAliasName ( Dest), CurrVarByte) + ",SysValueCopy")
            Else
              ' CurrLine = LinkedListInsert(CurrLine, "; IOAVR #1")
              CurrLine = LinkedListInsert(CurrLine, " mov " + GetByte(Dest, CurrVarByte) + ",SysValueCopy")
            End If
          Else
            'Note: If writing to a register, ldi then mov must be used
            'This will be optimised to ldi only later
            'If no ldi is present, will have just a mov later
            If LastConst = -1 Or LastConst <> ThisConst Then
              CurrLine = LinkedListInsert(CurrLine, " ldi SysValueCopy," + STemp)
              AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
              LastConst = ThisConst
            End If
            If DestIO Then
              CurrLine = LinkedListInsert(CurrLine, " out " + GetByte(Dest, CurrVarByte) + ",SysValueCopy")
            Else
              CurrLine = LinkedListInsert(CurrLine, " sts " + GetByte(Dest, CurrVarByte) + ",SysValueCopy")
             End If
          End If
        End If

      'Source is a variable
      Else
        'Copy byte from variable
        If ModePIC Then
          If ChipFamily = 16 Then
            CurrLine = LinkedListInsert(CurrLine, " movff " + GetByte(Source, CurrVarByte) + "," + GetByte(Dest, CurrVarByte))
          Else
            CurrLine = LinkedListInsert(CurrLine, " movf " + GetByte(Source, CurrVarByte) + ",W")
            CurrLine = LinkedListInsert(CurrLine, " movwf " + GetByte(Dest, CurrVarByte))
          End If
        ElseIf ModeAVR Then
          If DestReg Then
            If SourceReg Then
              CurrLine = LinkedListInsert(CurrLine, " mov " + GetByte(Dest, CurrVarByte) + "," + GetByte(Source, CurrVarByte))
            ElseIf SourceIO Then
              CurrLine = LinkedListInsert(CurrLine, " in " + GetByte(Dest, CurrVarByte) + "," + GetByte(Source, CurrVarByte))
            Else
              CurrLine = LinkedListInsert(CurrLine, " lds " + GetByte(Dest, CurrVarByte) + "," + GetByte(Source, CurrVarByte))
            End If
          ElseIf DestIO Then
            If SourceReg Then
              CurrLine = LinkedListInsert(CurrLine, " out " + GetByte(Dest, CurrVarByte) + "," + GetByte(Source, CurrVarByte))
            ElseIf SourceIO Then
              CurrLine = LinkedListInsert(CurrLine, " in SysValueCopy," + GetByte(Source, CurrVarByte))
              CurrLine = LinkedListInsert(CurrLine, " out " + GetByte(Dest, CurrVarByte) + ",SysValueCopy")
              AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
            Else
              CurrLine = LinkedListInsert(CurrLine, " lds SysValueCopy," + GetByte(Source, CurrVarByte))
              CurrLine = LinkedListInsert(CurrLine, " out " + GetByte(Dest, CurrVarByte) + ",SysValueCopy")
              AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
            End If
          Else
            If SourceReg Then
              CurrLine = LinkedListInsert(CurrLine, " sts " + GetByte(Dest, CurrVarByte) + "," + GetByte(Source, CurrVarByte))
            ElseIf SourceIO Then
              CurrLine = LinkedListInsert(CurrLine, " in SysValueCopy," + GetByte(Source, CurrVarByte))
              CurrLine = LinkedListInsert(CurrLine, " sts " + GetByte(Dest, CurrVarByte) + ",SysValueCopy")
              AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
            Else
              CurrLine = LinkedListInsert(CurrLine, " lds SysValueCopy," + GetByte(Source, CurrVarByte))
              CurrLine = LinkedListInsert(CurrLine, " sts " + GetByte(Dest, CurrVarByte) + ",SysValueCopy")
              AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
            End If
          End If
        End If

      End If

    Next

    'Variable copied, all done!
    Return OutList
  End If

  'Copies to or from non-integer types
  'Print "Old: "; Dest; " = "; Source,
  'Print "Source type =";  SType
  Select Case DType
  'Copy to bit
  Case "BIT":
    DestTemp = FixBit(Dest, Origin)
    If InStr(DestTemp, ".") <> 0 Then Replace DestTemp, ".", ","

    'Split dest into var and bit, get source
    Dim As String DestVarName, DestVarBit, IndDestVarName, IndDestVarBit, IndDestTemp
    If InStr(DestTemp, ",") <> 0 Then
      DestVarName = Trim(Left(DestTemp, InStr(DestTemp, ",") - 1))
      DestVarBit = Trim(Mid(DestTemp, InStr(DestTemp, ",") + 1))
    Else
      Print "Internal error in CompileVarSet bit > bit: " + DestTemp
    End If

    'Glitch free output or indirect mode needed?
    DestVarBitNo = -1
    UseIndirectBitSet = 0
    If IsConst(DestVarBit) Then
      DestVarBitNo = MakeDec(DestVarBit)
    Else
      SysVarBit = HashMapGet(SysVarBits, DestVarBit)
      If SysVarBit <> 0 Then
        DestVarBitNo = SysVarBit->Location
      Else
       'Test for indirect mode needed

       'add a test to set if the source is is an array element
        If instr( Source, "INDF" ) > 0 then
          Temp = Message("CannotUseArayBitasSource")

          LogError Temp, Origin
        End If

        If instr( Dest, "INDF" ) > 0 then
          Temp = Message("CannotUseArayBitasDestination")

          LogError Temp, Origin
        End If


        'Check the source is a bit or const - 060621#
        If SType = "BIT" Then
          'Need to use indirect bit setting
          UseIndirectBitSet = -1
        ElseIf SType = "CONST" And ( trim(Source) = "1" Or trim(Source) = "0" )  Then
          UseIndirectBitSet = -1
        Else
          Temp = Message("NotABitORAConst")
          Replace Temp, "%value%", Source
          LogError Temp, Origin
        End If
      End If
    End If
    RequiresGlitchFree = 0
    If DestVarBitNo <> -1 Then
      RequiresGlitchFree = HashMapGet(GlitchFreeOutputs, DestVarName + "." + Str(DestVarBitNo)) <> 0
    End If

    'Redirect PORTx writes to LATx
    If UseChipOutLatches And Left(DestTemp, 4) = "PORT" And Mid(DestTemp, 6, 1) = "," Then
      If HasSFR(DestVarName) Then
        DestTemp = "LAT" + Right(DestVarName, 1) + Mid(DestTemp, InStr(DestTemp, ","))
      End If
    End If

    'Record setting of individual OPTION_REG bits (PIC12x5/16x5)
    If ChipFamily = 12 Then
      If DestVarName = "OPTION_REG" And Not HasSFR(DestVarName) Then
        For CD = 1 To PinDirShadows
          If PinDirShadow(CD) = DestVarName Then GoTo OptionShadowFound
        Next
        PinDirShadows += 1
        PinDirShadow(PinDirShadows) = DestVarName
        OptionShadowFound:
      End If
    End If

    'Add var that contains bit
    IF INSTR(DestTemp, ",") <> 0 Then
      AddVar DestVarName, "BYTE", 1, DestSub, "REAL", Origin
    End If

    Select Case SType
    'bit > bit
      Case "BIT":
      SourceTemp = FixBit(Source, Origin)
      IF INSTR(SourceTemp, ".") <> 0 Then
        Temp = Left(SourceTemp, InStr(SourceTemp, ".") - 1)
        AddVar Temp, "BYTE", 1, CurrentSub, "REAL", Origin
      End If

      If ModePIC Then
        If UseIndirectBitSet Then
          IndDestTemp = DestTemp
          Replace IndDestTemp, ",", "."
          DestTemp = "SysByteTempB,0"
        End If

        'If INSTR(SourceTemp, ".") <> 0 Then Replace SourceTemp, ".", ","
        If RequiresGlitchFree Then
          ILC += 1
          'CurrLine = LinkedListInsert(CurrLine, " btfsc " + SourceTemp)
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(SourceTemp + "=1", "TRUE", Origin))
          CurrLine = LinkedListInsert(CurrLine, " goto ENDIF" + Str(ILC))
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " bsf " + DestTemp)
          Else
            CurrLine = LinkedListInsert(CurrLine, " bcf " + DestTemp)
          End If
          CurrLine = LinkedListInsert(CurrLine, "ENDIF" + Str(ILC))
          GetMetaData(CurrLine)->IsLabel = -1
        Else
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " bsf " + DestTemp)
          Else
            CurrLine = LinkedListInsert(CurrLine, " bcf " + DestTemp)
          End If
        End If
        ILC += 1
        'CurrLine = LinkedListInsert(CurrLine, " btfss " + SourceTemp)
        CurrLine = LinkedListInsertList(CurrLine, CompileConditions(SourceTemp + "=0", "TRUE", Origin))
        CurrLine = LinkedListInsert(CurrLine, " goto ENDIF" + Str(ILC))
        If InvertBitCopy Then
          CurrLine = LinkedListInsert(CurrLine, " bcf " + DestTemp)
        Else
          CurrLine = LinkedListInsert(CurrLine, " bsf " + DestTemp)
        End If
        CurrLine = LinkedListInsert(CurrLine, "ENDIF" + Str(ILC))
        GetMetaData(CurrLine)->IsLabel = -1

        If UseIndirectBitSet Then
          CurrLine = LinkedListInsertList(CurrLine, GenerateBitSet(IndDestTemp, "", Origin, DestSub, 0))
        End If

      ElseIf ModeAVR Then

        If UseIndirectBitSet Then
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " sbr SysByteTempB,1")
          Else
            CurrLine = LinkedListInsert(CurrLine, " cbr SysByteTempB,1")
          End If
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(SourceTemp + "=1", "TRUE", Origin))
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " cbr SysByteTempB,1")
          Else
            CurrLine = LinkedListInsert(CurrLine, " sbr SysByteTempB,1")
          End If
          CurrLine = LinkedListInsertList(CurrLine, GenerateBitSet(DestVarName + "." + DestVarBit, "", Origin, DestSub, 0))
          If Not IsIOReg(DestVarName) Then AddVar DestVarName, "BYTE", 1, 0, "REAL", Origin

        ElseIf IsRegister(DestVarName) Then
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " sbr " + DestVarName + ",1<<" + DestVarBit)
          Else
            CurrLine = LinkedListInsert(CurrLine, " cbr " + DestVarName + ",1<<" + DestVarBit)
          End If
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(SourceTemp + "=1", "TRUE", Origin))
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " cbr " + DestVarName + ",1<<" + DestVarBit)
          Else
            CurrLine = LinkedListInsert(CurrLine, " sbr " + DestVarName + ",1<<" + DestVarBit)
          End If

        ElseIf IsLowIOReg(DestVarName) Then
          If RequiresGlitchFree Then
            CurrLine = LinkedListInsertList(CurrLine, CompileConditions(SourceTemp + "=0", "TRUE", Origin))
          End If
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " sbi " + DestVarName + "," + DestVarBit)
          Else
            CurrLine = LinkedListInsert(CurrLine, " cbi " + DestVarName + "," + DestVarBit)
          End If
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(SourceTemp + "=1", "TRUE", Origin))
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " cbi " + DestVarName + "," + DestVarBit)
          Else
            CurrLine = LinkedListInsert(CurrLine, " sbi " + DestVarName + "," + DestVarBit)
          End If

        ElseIf LCase(DestVarName) = "sreg" Then
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " se" + LCase(Left(DestVarBit, 1)))
          Else
            CurrLine = LinkedListInsert(CurrLine, " cl" + LCase(Left(DestVarBit, 1)))
          End If
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(SourceTemp + "=1", "TRUE", Origin))
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " cl" + LCase(Left(DestVarBit, 1)))
          Else
            CurrLine = LinkedListInsert(CurrLine, " se" + LCase(Left(DestVarBit, 1)))
          End If

        Else
          If ChipFamily <> 121 then
            CurrLine = LinkedListInsert(CurrLine, " lds SysValueCopy," + DestVarName)
          Else
            'Assumes the DestVarName address is 0 = A = 63
            CurrLine = LinkedListInsert(CurrLine, " in SysValueCopy," + DestVarName)
          End if
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " sbr SysValueCopy,1<<" + DestVarBit)
          Else
            CurrLine = LinkedListInsert(CurrLine, " cbr SysValueCopy,1<<" + DestVarBit)
          End If
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(SourceTemp + "=1", "TRUE", Origin))
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " cbr SysValueCopy,1<<" + DestVarBit)
          Else
            CurrLine = LinkedListInsert(CurrLine, " sbr SysValueCopy,1<<" + DestVarBit)
          End If
          If ChipFamily <> 121 then
            CurrLine = LinkedListInsert(CurrLine, " sts " + DestVarName + ",SysValueCopy")
          else
            'Assumes the DestVarName is 0 = A = 63
            CurrLine = LinkedListInsert(CurrLine, " out " + DestVarName + ",SysValueCopy")
          End if
          If Not IsIOReg(DestVarName) Then AddVar DestVarName, "BYTE", 1, 0, "REAL", Origin
          AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
        End If

      End If

    'byte > bit / word > bit / integer > bit
      Case "BYTE", "WORD", "INTEGER", "LONG":
      If ModePIC Then
        If Not RequiresGlitchFree Then
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " bsf " + DestTemp)
          Else
            CurrLine = LinkedListInsert(CurrLine, " bcf " + DestTemp)
          End If
        End If
        ILC += 1
        CurrLine = LinkedListInsert(CurrLine, " btfss " + Source +",0")
        CurrLine = LinkedListInsert(CurrLine, " goto ENDIF" + Str(ILC))
        If InvertBitCopy Then
          CurrLine = LinkedListInsert(CurrLine, " bcf " + DestTemp)
        Else
          CurrLine = LinkedListInsert(CurrLine, " bsf " + DestTemp)
        End If
        CurrLine = LinkedListInsert(CurrLine, "ENDIF" + Str(ILC))
        GetMetaData(Currline)->IsLabel = -1
        If RequiresGlitchFree Then
          ILC += 1
          CurrLine = LinkedListInsert(CurrLine, " btfsc " + Source +",0")
          CurrLine = LinkedListInsert(CurrLine, " goto ENDIF" + Str(ILC))
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " bsf " + DestTemp)
          Else
            CurrLine = LinkedListInsert(CurrLine, " bcf " + DestTemp)
          End If
          CurrLine = LinkedListInsert(CurrLine, "ENDIF" + Str(ILC))
          GetMetaData(Currline)->IsLabel = -1
        End If
      ElseIf ModeAVR Then
        SourceTemp = Source

        If ChipAVRDX Then        
          If IsIORegDX(DestVarName) Then
            'Map legacy megaAVR register to DX register using the Alias Addressing
            If (( compilerdebug and cAVRDXDEBUG ) = cAVRDXDEBUG )  Then
              CurrLine = LinkedListInsert(CurrLine, ";IOAVRDX #4")
            End if
            DestVarName = GetSysVarAliasName ( DestVarName ) 
          End If   
        End If
        
        If IsRegister(DestVarName) Then
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " sbr " + DestVarName + ",1<<" + DestVarBit)
          Else
            CurrLine = LinkedListInsert(CurrLine, " cbr " + DestVarName + ",1<<" + DestVarBit)
          End If
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(SourceTemp + ".0~0", "TRUE", Origin))
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " cbr " + DestVarName + ",1<<" + DestVarBit)
          Else
            CurrLine = LinkedListInsert(CurrLine, " sbr " + DestVarName + ",1<<" + DestVarBit)
          End If

        ElseIf IsLowIOReg(DestVarName) Then
          If RequiresGlitchFree Then
            CurrLine = LinkedListInsertList(CurrLine, CompileConditions(SourceTemp + ".0=0", "TRUE", Origin))
          End If
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " sbi " + DestVarName + "," + DestVarBit)
          Else
            CurrLine = LinkedListInsert(CurrLine, " cbi " + DestVarName + "," + DestVarBit)
          End If
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(SourceTemp + ".0~0", "TRUE", Origin))
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " cbi " + DestVarName + "," + DestVarBit)
          Else
            CurrLine = LinkedListInsert(CurrLine, " sbi " + DestVarName + "," + DestVarBit)
          End If

        ElseIf LCase(DestVarName) = "sreg" Then
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " se" + LCase(Left(DestVarBit, 1)))
          Else
            CurrLine = LinkedListInsert(CurrLine, " cl" + LCase(Left(DestVarBit, 1)))
          End If
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(SourceTemp + ".0~0", "TRUE", Origin))
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " cl" + LCase(Left(DestVarBit, 1)))
          Else
            CurrLine = LinkedListInsert(CurrLine, " se" + LCase(Left(DestVarBit, 1)))
          End If

        Else
          CurrLine = LinkedListInsert(CurrLine, " lds SysValueCopy," + DestVarName)
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " sbr SysValueCopy,1<<" + DestVarBit)
          Else
            CurrLine = LinkedListInsert(CurrLine, " cbr SysValueCopy,1<<" + DestVarBit)
          End If
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(SourceTemp + ".0~0", "TRUE", Origin))
          If InvertBitCopy Then
            CurrLine = LinkedListInsert(CurrLine, " cbr SysValueCopy,1<<" + DestVarBit)
          Else
            CurrLine = LinkedListInsert(CurrLine, " sbr SysValueCopy,1<<" + DestVarBit)
          End If
          CurrLine = LinkedListInsert(CurrLine, " sts " + DestVarName + ",SysValueCopy")
          If Not IsIOReg(DestVarName) Then AddVar DestVarName, "BYTE", 1, 0, "REAL", Origin
          AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
        End If
      End If

    'single > bit
    Case "SINGLE":
      If ModePIC Then

      ElseIf ModeAVR Then

      End If

    'string > bit
    Case "STRING":
      If ModePIC Then

      ElseIf ModeAVR Then

      End If

    'const > bit
    Case "CONST":
      If MakeDec(Source) = 0 Then
        CurrLine = LinkedListInsertList(CurrLine, GenerateBitSet(FixBit(Dest, Origin), "0", Origin))
      Else
        CurrLine = LinkedListInsertList(CurrLine, GenerateBitSet(FixBit(Dest, Origin), "1", Origin))
      End If

    'sconst > bit
    Case "SCONST":
      ' support could be added this to catch silent compilation with no ASM  
      'Temp = Message("SynErrIncorrectBitDestination")
      'LogError Temp, Origin

      If ModePIC Then

      ElseIf ModeAVR Then

      End If

    End Select

  'Copy to byte
  Case "BYTE":
    AddVar Dest, "BYTE", 1, DestSub, "REAL", Origin
    Select Case SType
    'bit > byte
    Case "BIT"
      SourceTemp = FixBit(Source, Origin): If INSTR(SourceTemp, ".") <> 0 Then Replace SourceTemp, ".", ","

      If ModePIC Then
        ILC += 1
        CurrLine = LinkedListInsert(CurrLine, " clrf " + Dest)
        CurrLine = LinkedListInsert(CurrLine, " btfss " + SourceTemp)
        CurrLine = LinkedListInsert(CurrLine, " goto ENDIF" + Str(ILC))
        CurrLine = LinkedListInsert(CurrLine, " incf " + Dest + ",F")
        CurrLine = LinkedListInsert(CurrLine, "ENDIF" + Str(ILC))
        GetMetaData(CurrLine)->IsLabel = -1
      ElseIf ModeAVR Then
        If IsRegister(Dest) Then
          CurrLine = LinkedListInsert(CurrLine, " clr " + Dest)
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(Source + "=1", "TRUE", Origin))
          CurrLine = LinkedListInsert(CurrLine, " inc " + Dest)
        Else
          CurrLine = LinkedListInsert(CurrLine, " clr SysValueCopy")
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(Source + "=1", "TRUE", Origin))
          CurrLine = LinkedListInsert(CurrLine, " inc SysValueCopy")
          CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("SysValueCopy", Dest, Origin))
          AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
        End If
      End If

    'single > byte
    Case "SINGLE", "DOUBLE":
      DType = "LONG"
      AddVar "Sys" + SType + "Temp", SType, 1, 0, "REAL", "", , -1
      AddVar "Sys" + DType + "Temp", DType, 1, 0, "REAL", "", , -1
      RequestSub(CurrentSub, "SysConv" + SType + "To" + DType, "")
      CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(Source, "Sys" + SType + "Temp", Origin))
      CurrLine = LinkedListInsert(CurrLine, " call SysConv" + SType + "To" + DType)
      CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("Sys" + DType + "Temp", Dest, Origin))

    'string > byte
    Case "STRING":

      'Temp = Message("SynErrIncorrectByteDestination")
      'LogError Temp, Origin

      If ModePIC Then

      ElseIf ModeAVR Then

      End If

    'sconst > byte
    Case "SCONST":
      ' support could be added this to catch silent compilation with no ASM  
      'Temp = Message("SynErrIncorrectByteDestination")
      'LogError Temp, Origin

      If ModePIC Then

      ElseIf ModeAVR Then

      End If

    End Select

  'Copy to word or integer
  Case "WORD", "INTEGER":
    Select Case SType
    'bit > word
    Case "BIT":
      SourceTemp = FixBit(Source, Origin): If INSTR(SourceTemp, ".") <> 0 Then Replace SourceTemp, ".", ","

      If ModePIC Then
        ILC += 1
        CurrLine = LinkedListInsert(CurrLine, " clrf " + Dest)
        CurrLine = LinkedListInsert(CurrLine, " clrf " + Dest + "_H")
        CurrLine = LinkedListInsert(CurrLine, " btfss " + SourceTemp)
        CurrLine = LinkedListInsert(CurrLine, " goto ENDIF" + Str(ILC))
        CurrLine = LinkedListInsert(CurrLine, " incf " + Dest + ",F")
        CurrLine = LinkedListInsert(CurrLine, "ENDIF" + Str(ILC))
      ElseIf ModeAVR Then
        If IsRegister(Dest) Then
          CurrLine = LinkedListInsert(CurrLine, " clr " + Dest)
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(Source + "=1", "TRUE", Origin))
          CurrLine = LinkedListInsert(CurrLine, " inc " + Dest)
          CurrLine = LinkedListInsert(CurrLine, " clr " + Dest + "_H")
        Else
          CurrLine = LinkedListInsert(CurrLine, " clr SysValueCopy")
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(Source + "=1", "TRUE", Origin))
          CurrLine = LinkedListInsert(CurrLine, " inc SysValueCopy")
          CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("SysValueCopy", Dest, Origin))
          AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
        End If
      End If

    'single > word
    Case "SINGLE", "DOUBLE":
      If DType = "WORD" Then DType = "LONG"
      AddVar "Sys" + SType + "Temp", SType, 1, 0, "REAL", "", , -1
      AddVar "Sys" + DType + "Temp", DType, 1, 0, "REAL", "", , -1
      RequestSub(CurrentSub, "SysConv" + SType + "To" + DType, "")
      CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(Source, "Sys" + SType + "Temp", Origin))
      CurrLine = LinkedListInsert(CurrLine, " call SysConv" + SType + "To" + DType)
      CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("Sys" + DType + "Temp", Dest, Origin))

    'string > word
    Case "STRING":

      'Temp = Message("SynErrIncorrectWordDestination")
      'LogError Temp, Origin

      If ModePIC Then

      ElseIf ModeAVR Then

      End If

    'sconst > word
    Case "SCONST":
      ' support could be added this to catch silent compilation with no ASM  
      'Temp = Message("SynErrIncorrectWordDestination")
      'LogError Temp, Origin

      If ModePIC Then

      ElseIf ModeAVR Then

      End If

    End Select

  'Copy to long
  Case "LONG":
    Select Case SType
    'bit > long
    Case "BIT":
      SourceTemp = FixBit(Source, Origin): If INSTR(SourceTemp, ".") <> 0 Then Replace SourceTemp, ".", ","

      If ModePIC Then
        ILC += 1
        CurrLine = LinkedListInsert(CurrLine, " clrf " + Dest)
        CurrLine = LinkedListInsert(CurrLine, " clrf " + Dest + "_H")
        CurrLine = LinkedListInsert(CurrLine, " clrf " + Dest + "_U")
        CurrLine = LinkedListInsert(CurrLine, " clrf " + Dest + "_E")
        CurrLine = LinkedListInsert(CurrLine, " btfss " + SourceTemp)
        CurrLine = LinkedListInsert(CurrLine, " goto ENDIF" + Str(ILC))
        CurrLine = LinkedListInsert(CurrLine, " incf " + Dest + ",F")
        CurrLine = LinkedListInsert(CurrLine, "ENDIF" + Str(ILC))
      ElseIf ModeAVR Then
        If IsRegister(Dest) Then
          CurrLine = LinkedListInsert(CurrLine, " clr " + Dest)
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(Source + "=1", "TRUE", Origin))
          CurrLine = LinkedListInsert(CurrLine, " inc " + Dest)
          CurrLine = LinkedListInsert(CurrLine, " clr " + Dest + "_H")
          CurrLine = LinkedListInsert(CurrLine, " clr " + Dest + "_U")
          CurrLine = LinkedListInsert(CurrLine, " clr " + Dest + "_E")
        Else
          CurrLine = LinkedListInsert(CurrLine, " clr SysValueCopy")
          CurrLine = LinkedListInsertList(CurrLine, CompileConditions(Source + "=1", "TRUE", Origin))
          CurrLine = LinkedListInsert(CurrLine, " inc SysValueCopy")
          CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("SysValueCopy", Dest, Origin))
          AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
        End If
      End If

    'single > long
    Case "SINGLE", "DOUBLE":
      AddVar "Sys" + SType + "Temp", SType, 1, 0, "REAL", "", , -1
      AddVar "Sys" + DType + "Temp", DType, 1, 0, "REAL", "", , -1
      RequestSub(CurrentSub, "SysConv" + SType + "To" + DType, "")
      CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(Source, "Sys" + SType + "Temp", Origin))
      CurrLine = LinkedListInsert(CurrLine, " call SysConv" + SType + "To" + DType)
      CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("Sys" + DType + "Temp", Dest, Origin))

    'string > long
    Case "STRING":

      'Temp = Message("SynErrIncorrectLongDestination")
      'LogError Temp, Origin

      If ModePIC Then

      ElseIf ModeAVR Then

      End If

    'sconst > long
    Case "SCONST":
      ' support could be added this to catch silent compilation with no ASM  
      'Temp = Message("SynErrIncorrectLongDestination")
      'LogError Temp, Origin

      If ModePIC Then

      ElseIf ModeAVR Then

      End If

    End Select

  'Copy to floating point type (single or double)
  Case "SINGLE", "DOUBLE"
    DestIsDouble = DType = "DOUBLE"
    Select Case SType
    'bit > single
    Case "BIT":
      If ModePIC Then

      ElseIf ModeAVR Then

      End If

    'byte/word/long/integer > single/double
    Case "BYTE", "WORD", "LONG", "INTEGER":
      'Use long code for word, byte
      If SType = "BYTE" Or SType = "WORD" Then SType = "LONG"
      'Need to convert
      AddVar "Sys" + SType + "Temp", SType, 1, 0, "REAL", "", , -1
      AddVar "Sys" + DType + "Temp", DType, 1, 0, "REAL", "", , -1
      RequestSub(CurrentSub, "SysConv" + SType + "To" + DType, "")
      CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(Source, "Sys" + SType + "Temp", Origin))
      CurrLine = LinkedListInsert(CurrLine, " call SysConv" + SType + "To" + DType)
      CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("Sys" + DType + "Temp", Dest, Origin))

    'single/double > single/double
    Case "SINGLE", "DOUBLE":
      If SType = DType Then
        For CurrVarByte = 0 To GetTypeSize(SType) - 1
          CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("[byte]" + GetByte(Source, CurrVarByte), "[byte]" + GetByte(Dest, CurrVarByte), Origin))
        Next
      Else
        'Need to convert
        AddVar "SysSingleTemp", "SINGLE", 1, 0, "REAL", "", , -1
        AddVar "SysDoubleTemp", "DOUBLE", 1, 0, "REAL", "", , -1
        RequestSub(CurrentSub, "SysConv" + SType + "To" + DType, "")
        CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(Source, "Sys" + SType + "Temp", Origin))
        CurrLine = LinkedListInsert(CurrLine, " call SysConv" + SType + "To" + DType)
        CurrLine = LinkedListInsertList(CurrLine, CompileVarSet("Sys" + DType + "Temp", Dest, Origin))
      End If

    'string > single
    Case "STRING":

      'Temp = Message("SynErrIncorrectSingleDestination")
      'LogError Temp, Origin

      ' HANDLED ELSEWHERE    LogError "ErrorTemp 1", Origin

      If ModePIC Then

      ElseIf ModeAVR Then

      End If

    'const > single/double
    Case "CONST":
      If DestIsDouble Then
        Dim TempDoubleBytes As ULongInt
        TempDoubleBytes = GetDoubleBytes(Val(Source))
        For CurrVarByte = 0 To 7
          CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(Str((TempDoubleBytes Shr (CurrVarByte * 8)) And 255), "[byte]" + GetByte(Dest, CurrVarByte), Origin))
        Next
      Else
        OutList = CompileVarSet(Str(GetSingleBytes(Val(Source))), "[long]" + Dest, Origin)
      End If

    'sconst > single
    Case "SCONST":

      ' support could be added this to catch silent compilation with no ASM  
      'Temp = Message("SynErrIncorrectSingleDestination")
      'LogError Temp, Origin

      If ModePIC Then

      ElseIf ModeAVR Then

      End If

    End Select

  'Copy to string
  Case "STRING":
    OutList = CompileString(Dest + "=" + SourceIn, Origin)

  End Select

  Return OutList
  End Function

Sub CompileVars (CompSub As SubType Pointer)
  Dim As String InLine, Origin, Temp, DestVar, SourceData
  Dim As String VarType, SourceOld, ErrorTemp, OriginalDest
  Dim As Integer CD, PD, T, DisableInt, CTR, AIC, CanSkip
  Dim As PinDirType Pointer CurrPinDir
  Dim As SubType Pointer SourceSub, DestSub

  Dim As LinkedListElement Pointer CurrLine, NewCode, FindLine
  Dim As CodeSection Pointer NewSection

  CurrLine = CompSub->CodeStart->Next
  FoundCount = 0
  Do While CurrLine <> 0
    InLine = CurrLine->Value

    If INSTR(InLine, "=") <> 0 THEN
      'Don't try to do anything if = follows "dw" or "retlw"
      IF INSTR(LCase(InLine), " dw ") <> 0 THEN GOTO CompNextVar
      IF INSTR(LCase(InLine), " retlw ") <> 0 THEN GOTO CompNextVar
      IF Left(InLine, 4) = "ASM " Then GOTO CompNextVar

      'Allow overflow?
      AllowOverflow = 0
      If INSTR(LCase(InLine), "[ao]") <> 0 Then
        Replace InLine, "[ao]", ""
        AllowOverflow = 1
      End If

      'Only try to set, skip if trying to set non-existent register
      CanSkip = 0
      If INSTR(LCase(InLine), "[canskip]") <> 0 Then
        Replace InLine, "[canskip]", ""
        CanSkip = -1
      End If

      'Get origin and dest sub
      Origin = ""
      IF INSTR(InLine, ";?F") <> 0 THEN
        Origin = Mid(InLine, INSTR(InLine, ";?F"))
        InLine = RTrim(Left(InLine, INSTR(InLine, ";?F") - 1))
        CurrLine->Value = InLine
      END IF
      If Origin <> "" Then
        SourceSub = Subroutine(GetSubID(Origin))
      Else
        SourceSub = CompSub
      End If
      IF INSTR(Origin, "D") = 0 Then
        DestSub = SourceSub
      Else
        DestSub = Subroutine(GetDestSub(Origin))
      End If

      'Remove ALL spaces
      DO WHILE INSTR(InLine, " ") <> 0: Replace InLine, " ", "": LOOP

      'Get source and destination
      DestVar = Left(InLine, INSTR(InLine, "=") - 1)
      SourceData = Mid(InLine, INSTR(InLine, "=") + 1)

      'Ensure that a variable is being set
      Temp = DestVar
      IF INSTR(Temp, ".") <> 0 THEN Temp = Left(Temp, INSTR(Temp, ".") - 1)
      If IsConst(Temp) THEN
        ErrorTemp = Message("NotAVariable")
        Replace ErrorTemp, "%value%", Temp
        LogError ErrorTemp, Origin
        GoTo CompNextVar
      END IF

      'Load an array with a list of values?
      If INSTR(SourceData, ",") <> 0 And IsArray(DestVar, DestSub) Then
        CurrLine = LinkedListDelete(CurrLine)
        CurrLine = LinkedListInsertList(CurrLine, CompileWholeArray(InLine, Origin))
        GOTO CompNextVar
      END If

      'Load multiple bits?
      If InStr(DestVar, ",") <> 0 Then
        CurrLine = LinkedListDelete(CurrLine)
        CurrLine = LinkedListInsertList(CurrLine, GenerateMultiSet(SourceData, DestVar, Origin, CompSub, CanSkip))
        GoTo CompNextVar
      EndIf

      'Check if command involves setting a string
      IF IsString(DestVar, DestSub) Or INSTR(DestVar, "()") <> 0 Then
            CurrLine = LinkedListDelete(CurrLine)
            CurrLine = LinkedListInsertList(CurrLine, CompileString(InLine, Origin))
            GOTO CompNextVar
      END IF

      'Call another Sub to generate code
      If IsCalc(SourceData) Then

        'Use temporary variable? (Needed for bit destinations)
        OriginalDest = ""
        If InStr(DestVar, ".") <> 0 Then
          OriginalDest = DestVar
          DestVar = GetCalcVar("BYTE")
        End If

        '@1094 add assignment check to prevent target var being corrupted when same var as target is in the calcluation
        'Delay_time_i = (Descend_start_i - Delay_time_i)
        'See AssignmentDeductionTest.gcb or AssignmentAddititionTest.gcb

        If HashMapGet(Constants, "DISABLE1094" ) = 0 then

          If ModePIC and ChipFamily=14 THEN
            'resolves Family=14 maths error

            Dim as integer sourcevariablepos, pospos, negpos, nextchar
            Dim as string ParamArray()
            sourcevariablepos=  INSTR ( SourceData, DestVar)
            If CountOccur(SourceData, "';=~<>{}*/%&|#!")  = 0 AND sourcevariablepos <> 0 AND LEFT(DestVar,3)<>"SYS" AND LEFT(DestVar,3)<>"AFR"   THEN
              If  TypeOfVar(DestVar, Subroutine(GetSubID(Origin))) <> "BYTE" THEN
                  pospos = INSTR( SourceData ,"+")
                  negpos = INSTR( SourceData ,"-")

                  nextchar = 48
                  If pospos > 1 AND CountOccur(SourceData, "+")  = 1 THEN
                      StringSplit ( SourceData, "+",-1,ParamArray() )
                      replace( ParamArray(0),"(","" )
                      replace( ParamArray(1),"(","" )
                      replace( ParamArray(0),")","" )
                      replace( ParamArray(1),")","" )
                      If NOT (DestVar = ParamArray(0) or DestVar = ParamArray(1) ) then Goto NoMatchofParameter

                      If pospos < sourcevariablepos THEN
                        nextchar = ASC(Mid ( SourceData, pospos +1,1 ))
                      End if

                      'If nextchar <> 43 or nextchar <> 45 Then nextchar = 48  'assumed not to be a perfect match but a substring
                  ElseIf negpos > 1 AND CountOccur(SourceData, "-")  = 1 THEN
                      StringSplit ( SourceData, "-",-1,ParamArray() )
                      replace( ParamArray(0),"(","" )
                      replace( ParamArray(1),"(","" )
                      replace( ParamArray(0),")","" )
                      replace( ParamArray(1),")","" )
                      If NOT ( DestVar = ParamArray(0) or DestVar = ParamArray(1) ) then Goto NoMatchofParameter

                      If negpos < sourcevariablepos THEN
                        nextchar = ASC(Mid ( SourceData, negpos +1,1 ))
                      End if
                      'If nextchar <> 43 or nextchar <> 45 Then nextchar = 48  'assumed not to be a perfect match but a substring
                  Else
                      nextchar = 48  'a numberic or complex
                  End if


                  if nextchar < 48 OR nextchar > 58  Then
                      OriginalDest = DestVar

                      If HashMapGet(Constants, "DEBUG1094" ) <> 0 then
                        LogWarning TypeOfVar(DestVar, Subroutine(GetSubID(Origin)))+" " +Destvar + " = " +SourceData, origin
                      End if
                      DestVar = GetCalcVar( TypeOfVar(DestVar, Subroutine(GetSubID(Origin))) )
                  End if
              End if
            End If
          End if
        End if
        NoMatchofParameter:


        'Add var
        If InStr(DestVar, "[") = 0 Then
          AddVar DestVar, "BYTE", 1, DestSub, "REAL", Origin
          VarType = TypeOfVar(DestVar, DestSub)
        Else
          VarType = Mid(DestVar, InStr(DestVar, "[") + 1)
          VarType = Left(VarType, InStr(VarType, "]") - 1)
        End If

        'Compile calculation
        FreeCalcVar ""
        SourceOld = SourceData
        NewSection = 0
        CompileCalc SourceData, DestVar, Origin, NewSection

        'Finish Calculation
        'If no code has been produced, set var to final value
        IF NewSection->CodeList = 0 THEN
          NewCode = CompileVarSet(SourceData, DestVar, Origin)
        Else
          NewCode = NewSection->CodeList

          CurrPinDir = GetPinDirection(DestVar)
          If CurrPinDir <> 0 Then
            CurrPinDir->WrittenTo = -1
          End If
        END IF

        If ModePIC Then
          'Get last line
          FindLine = NewCode->Next
          If FindLine <> 0 Then
            Do While FindLine->Next <> 0
              FindLine = FindLine->Next
            Loop
            'Change destination of last operation
            Temp = FindLine->Prev->Value
            IF WholeINSTR(Temp, DelType(DestVar)) = 2 AND INSTR(Temp, "wf") <> 0 And INSTR(Temp, " movwf ") = 0 And INSTR(Temp, " mulwf ") = 0 Then
              FindLine = LinkedListDelete(FindLine)
              'The test below was added to protect the incorrection replacement operation 1260
              If INSTR( FindLine->Value, ",W") Then
                Replace FindLine->Value, ",W", ",F"
              End If
            END If
          End If
        End If

        'Copy temp var to output (if needed)
        If OriginalDest <> "" Then
          'Get last line
          FindLine = NewCode->Next
          If FindLine <> 0 Then
            Do While FindLine->Next <> 0
              FindLine = FindLine->Next
            Loop

            LinkedListInsertList(FindLine, CompileVarSet(DestVar, OriginalDest, Origin))
          End If
        End If

        'Write the assembly code
        If NewCode->Next <> 0 Then
          CurrLine = LinkedListDelete(CurrLine)
          'Isolation of 1094 fix for PIC only
          If HashMapGet(Constants, "DISABLE1094" ) = 0 then
            If ModePic  and ChipFamily=14 THEN
              If PreserveMode <> 0 Then
                ' Commented out to remove clutter from ASM.. no one has complained so change 1094 must work ok
                ' CurrLine = LinkedListInsert(CurrLine, ";Using temp var method implemented at #1094. Use a constant DISABLE1094 to disable")
              End If
            End if
          End if
          CurrLine = LinkedListInsertList(CurrLine, NewCode)
        Else
          CurrLine = LinkedListDelete(CurrLine)
        End If

      Else
        'Not a calculation, compile the variable setting operation
        'Trim brackets from SourceData
        If Left(SourceData, 1) = "(" And Right(SourceData, 1) = ")" And CountOccur(SourceData, "';()") = 2 Then
          SourceData = Mid(SourceData, 2, Len(SourceData) - 2)
        End If

        'Add variables
        AddVar SourceData, "BYTE", 1, SourceSub, "REAL", Origin
        AddVar DestVar, "BYTE", 1, DestSub, "REAL", Origin

        'Get code
        NewCode = CompileVarSet(SourceData, DestVar, Origin)

        'Add code
        If NewCode->Next <> 0 Then
          CurrLine = LinkedListDelete(CurrLine)
          CurrLine = LinkedListInsertList(CurrLine, NewCode)
        Else
          CurrLine = LinkedListDelete(CurrLine)
        End If

      End If

      FoundCount = FoundCount + 1
    END IF

  CompNextVar:
    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub CompileWait (CompSub As SubType Pointer)

  'Time Intervals: us, 10us, ms, 10ms, s, m, h
  Dim As String InLine, Origin, Temp, Value, Unit, Condition
  Dim As Integer UP, T, Cycles, DS, ExpandedValueLen, lUSDelaysInaccurate, overrideUSDelaysInaccurateWarning
  Dim as Integer lValueASC, LessOneCycle
  Dim As LinkedListElement Pointer CurrLine, NewCode
  Dim ExpandedValue as String
  Dim minDelay as String

  FoundCount = 0
  
  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    InLine = UCase(CurrLine->Value)

    IF Left(InLine, 5) = "WAIT " or Left(InLine, 7) = "WAITL1 " THEN

      overrideUSDelaysInaccurateWarning = 0

      IF Left(InLine, 7) = "WAITL1 " Then
        Replace Inline, "WAITL1", "WAIT"
        LessOneCycle = 1
      Else
        LessOneCycle = 0
      End if

      If INSTR( Inline, "#OVERRIDEWARNING") > 0 Then
        overrideUSDelaysInaccurateWarning = -1
        Replace ( Inline, "#OVERRIDEWARNING", "" )
      End If

      Origin = ""
      IF INSTR(InLine, ";?F") <> 0 THEN
        Origin = Mid(InLine, INSTR(InLine, ";?F"))
        InLine = RTrim(Left(InLine, INSTR(InLine, ";?F") - 1))
      END IF

      Value = Mid(InLine, 6)
      IF Left(UCase(Value), 6) = "WHILE " OR Left(UCase(Value), 6) = "UNTIL " THEN
        Unit = LCase(Mid(Value, INSTR(Value, " ") + 1))
        Value = UCase(Left(Value, INSTR(Value, " ") - 1))
      Else
        Value = Trim(Value)
        FOR UP = LEN(Value) TO 1 STEP -1
          IF Mid(Value, UP, 1) = " " THEN
            Unit = LCase(Trim(Mid(Value, UP + 1)))
            Value = Trim(Left(Value, UP - 1))
            EXIT FOR
          END IF
        NEXT
      END IF

      'Time-based wait
      IF Value <> "UNTIL" AND Value <> "WHILE" THEN
        IF INSTR(Unit, "second") <> 0 THEN Replace Unit, "second", "s"
        Select Case Unit
        Case "us", "usec":
          Unit = "Delay_US"
        Case "10us", "10usec":
          Unit = "Delay_10US"
        Case "ms", "msec":
          Unit = "Delay_MS"
        Case "10ms", "10msec":
          Unit = "Delay_10MS"
        Case "s", "sec":
          Unit = "Delay_S"
        Case "m", "min":
          Unit = "Delay_M"
        Case "h", "hour":
          Unit = "Delay_H"
        Case Else:
          LogError Message("NoDelayUnits"), Origin
          CurrLine = LinkedListDelete(CurrLine)
          GoTo EndWaitCompile
        End Select

        Temp = Unit: Replace Temp, "Delay_", ""

        'Add word variables (PIC only)
        'If ModePIC Then
          If Unit = "Delay_US" Then AddVar "SysWaitTempUS", "WORD", 1, 0, "REAL", Origin,, -1
          If Unit = "Delay_MS" Then AddVar "SysWaitTempMS", "WORD", 1, 0, "REAL", Origin,, -1
          'Delays which depend on Delay_MS
          If Unit = "Delay_10MS" Then AddVar "SysWaitTempMS", "WORD", 1, 0, "REAL", Origin,, -1
          If Unit = "Delay_S" Then AddVar "SysWaitTempMS", "WORD", 1, 0, "REAL", Origin,, -1
          If Unit = "Delay_M" Then AddVar "SysWaitTempMS", "WORD", 1, 0, "REAL", Origin,, -1
          If Unit = "Delay_H" Then AddVar "SysWaitTempMS", "WORD", 1, 0, "REAL", Origin,, -1
        'End If

        'Check for delay of 0
        IF Trim(Value) = "0" OR VAL(Value) < 0 THEN
          CurrLine = LinkedListDelete(CurrLine)
          GOTO EndWaitCompile
        END If
        'Check for delay with floating point value
        If InStr(Value, ".") <> 0 Then
          If Trim(Value) = Trim(Str(Val(Value))) Then
            LogError Message("FloatInDelay"), Origin
            CurrLine = LinkedListDelete(CurrLine)
            GoTo EndWaitCompile
          End If
        End If
        'Patch #1041 10/10/2021
        'Check the syntax of the Value... does it have a space or other error?
        ExpandedValue = ReplaceConstantsLine(trim(Value),0)
        ExpandedValueLen = LEN(ExpandedValue)
        If InStr(ExpandedValue, " ") <> 0 Then  'The Value has spaces but this cound be a calc like 'show_timer / 4 ms'
          For UP = 1 to ExpandedValueLen
            lValueASC = ASC(MID(ExpandedValue,UP,1))
            'walk the Value looking for NON numeric values.. if non numeric then we can assume that we have numbers only and numbers can be tested
            If ( lValueASC < 47 OR  lValueASC > 57 ) and lValueASC <> 32 Then 'this is NOT a numeric constant
                Goto NoTNumericConstant
            End if
          Next
          'Test a numeric value of Value .. if Value <> Str(Val(Value)) then we have something like '10 10'... error!
          If trim(ExpandedValue) <> trim(Str(Val(ExpandedValue))) Then
              LogError Message("IncorrectWaitParameter"), Origin
              CurrLine = LinkedListDelete(CurrLine)
              GoTo EndWaitCompile
          End If
        End if
         NoTNumericConstant:
        'End of Patch #1041

        'Compile longer delays as subs
        IF Unit <> "Delay_US" OR Not IsConst(Value) Then
          CurrLine->Value = "SysWaitTemp" + Temp + "=" + Value + Origin
          CurrLine = LinkedListInsert(CurrLine, " call " + Unit)
          RequestSub(CompSub, Unit)

          'Generate error when using US delay on slow chips
          IF Unit = "Delay_US" AND gUSDelaysInaccurate  AND overrideUSDelaysInaccurateWarning = 0 THEN
            Temp = Message("WarningUSDelay")
            Temp = Temp + ": " + Value
            LogWarning Temp, Origin
          END If

        'Compile microsecond delays as inline when given constant length
        Else
          If ModePIC Then
            Cycles = MakeDec(Value) * ChipMHz / 4
          ElseIf ModeAVR Then
            Cycles = MakeDec(Value) * ChipMHz
          End If

          NewCode = GenerateExactDelay(Cycles - LessOneCycle)
          CurrLine = LinkedListDelete(CurrLine)

          'add message regarding minimum delay
          If Len( Trim(NewCode->Next->Value) ) = 0 Then
            If  ChipFamily <> 16 Then
                minDelay = str(INT((1/( ChipMhz /4)+2)/2))
            Else
                minDelay = str(INT((1/( ChipMhz /4)+2)/2))
            End if
            CurrLine = LinkedListInsert(CurrLine, ";GCASM note: wait unit and value too slow for chip frequency, no ASM added. Minimum delay is " + minDelay + "us")
          End if
          CurrLine = LinkedListInsertList(CurrLine, NewCode)

        END IF

        FoundCount = FoundCount + 1

      'Condition-based Wait
      Else
        Condition = UCase(Unit)
        WSC = WSC + 1
        CurrLine->Value = "SysWaitLoop" + Str(WSC) + LabelEnd
        GetMetaData(Currline)->IsLabel = -1
        'Get code
        Temp = "TRUE": IF Value = "UNTIL" THEN Temp = "FALSE"
        NewCode = CompileConditions(Condition, Temp, Origin)
        'Add
        CurrLine = LinkedListInsertList(CurrLine, NewCode)
        'Append appropriate goto
        If Condition <> "AlwaysFalse" Then
          If ModePIC Then CurrLine = LinkedListInsert(CurrLine, " goto SysWaitLoop" + Str(WSC))
          If ModeAVR Then CurrLine = LinkedListInsert(CurrLine, " rjmp SysWaitLoop" + Str(WSC))
        End If

        FoundCount = FoundCount + 1
      END IF

    END IF

  EndWaitCompile:
    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Function CompileWholeArray (InLine As String, Origin As String) As LinkedListElement Pointer

  Dim As String DestVar, Source, Temp, TempData
  Dim As String ArrayHandler, DestArrayHandler, ArrayName, DestArrayName
  Dim StringTemp(256) As String
  Dim As Integer AF, ArrayType, TDC, PD, MaxArrayElements
  Dim As LinkedListElement Pointer OutList, CurrLine
  Dim As SubType Pointer CurrSub

  OutList = LinkedListCreate
  CurrLine = OutList

  'Get subroutine
  CurrSub = Subroutine(GetSubID(Origin))

  'Fix extra ' as end of string
  Inline = trim(InLine)
  if right(Inline,1) = "," then
      Inline = MID(Inline,1,LEN(Inline)-1)
  end if


  'Find array address, display error if not found
  DestVar = Trim(Left(InLine, INSTR(InLine, "=") - 1))
  Source = Trim(Mid(InLine, INSTR(InLine, "=") + 1))

  'Set indirect pointer
  If ModePIC Then
    If ChipFamily = 15 Or ChipFamily = 16 Then
      CurrLine = LinkedListInsertList(CurrLine, GenerateArrayPointerSet(DestVar, 0, CurrSub, Origin))
    Else
      CurrLine = LinkedListInsertList(CurrLine, GenerateArrayPointerSet(DestVar, -1, CurrSub, Origin))
    End If
  ElseIf ModeAVR Then
    CurrLine = LinkedListInsertList(CurrLine, GenerateArrayPointerSet(DestVar, 0, CurrSub, Origin))
  End If

  'Prevent silent failure of the overrun of the StringTemp array
  If CHIPRAM < 2048 Then MaxArrayElements = 250 Else MaxArrayElements = 255

  'Get source data
  TDC = 0
  Do While INSTR(Source, ",") <> 0
    TDC += 1
    'Prevents silent failure of the overrun of the StringTemp array
    if TDC > MaxArrayElements - 1 then
        'Error for oversized array
        TempData = Message("TooManyArrayElements")
        Replace TempData, "%Array%", DestVar
        Replace TempData, "%Max%", str(MaxArrayElements)
        LogError TempData, Origin
    end if
    StringTemp(TDC) = Trim(Left(Source, INSTR(Source, ",") - 1))
    Source = Mid(Source, INSTR(Source, ",") + 1)

  Loop
  TDC += 1
  StringTemp(TDC) = Trim(Source)
  StringTemp(0) = Str(TDC)

  'Store source data
  For PD = 0 to TDC
    If ModePIC Then
      If ChipFamily = 12 Or ChipFamily = 14 Then
        'CurrLine = LinkedListInsert(CurrLine, "INDF = " + StringTemp(PD))
        CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(StringTemp(PD), "INDF", Origin))
        If PD < TDC Then CurrLine = LinkedListInsert(CurrLine, " incf FSR,F")
      ElseIf ChipFamily = 15 Then
        CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(StringTemp(PD), "INDF0", Origin))
        If PD < TDC Then CurrLine = LinkedListInsert(CurrLine, " incf FSR0L,F")
      ElseIf ChipFamily = 16 Then
        'CurrLine = LinkedListInsert(CurrLine, "POSTINC0 = " + StringTemp(PD))
        CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(StringTemp(PD), "POSTINC0", Origin))
      End If
    ElseIf ModeAVR Then
      CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(StringTemp(PD), "SysValueCopy", Origin))
      CurrLine = LinkedListInsert(CurrLine, " st X+, SysValueCopy")
    End If
  Next

  Return OutList
  End Function

Function ConfigNameMatch(ConfigIn As String, ConfigNameIn As String) As Integer

  'Ensure Equivalent config settings are ready
  If EqConfigSettings = 0 Then
    GetEqConfig
  End If

  'Checks whether an input config setting (ie MCLR_OFF) matches a name (ie MCLR)
  Dim As String Config, ConfigName
  Config = ConfigIn
  ConfigName = ConfigNameIn
  ReplaceAll Config, "_", " "
  ReplaceAll ConfigName, "_", " "

  'If = found, get whatever is before it
  If InStr(Config, "=") <> 0 Then Config = Trim(Left(Config, InStr(Config, "=") - 1))
  If InStr(ConfigName, "=") <> 0 Then ConfigName = Trim(Left(ConfigName, InStr(ConfigName, "=") - 1))

  'If name of setting in input config, name matches
  If WholeINSTR(Config, ConfigName) = 2 Then Return -1
  If WholeINSTR(ConfigName, Config) = 2 Then Return -1

  Dim As LinkedListElement Pointer CurrItem, Look1, Look2

  'Find equivalent names
  CurrItem = EqConfigSettings->Next
  Do While CurrItem <> 0

    'Look at every item in the list
    'Compare to every other item in the list
    Look1 = CurrItem->Metadata
    Look1 = Look1->Next
    Do While Look1 <> 0
      Look2 = CurrItem->Metadata
      Look2 = Look2->Next
      Do While Look2 <> 0
        If Look1 <> Look2 Then
          'Have two different but equivalent settings, check for them
          If ConfigName = Look1->Value And WholeINSTR(Config, Look2->Value) = 2 Then Return -1

        End If
        Look2 = Look2->Next
      Loop
      Look1 = Look1->Next
    Loop

    CurrItem = CurrItem->Next
  Loop

  If ConfigName = "OSC" And InStr(Config, "OSC") <> 0 Then Return -1

  'No match found
  Return 0
  End Function

Function ConfigValueMatch(ConfigIn As String, ConfigValueIn As String, MatchAny As Integer = 0) As Integer
  'MatchAny is set when checking if ConfigValueIn is any internal oscillator, not just the fastest one

  'Checks whether an input config setting (ie MCLR_OFF) matches a value (ie OFF)
  Dim As String Config, ConfigValue, CurrValue
  Dim As Integer CurrOption
  Config = ConfigIn
  ConfigValue = ConfigValueIn
  ReplaceAll Config, "_", " "
  ReplaceAll ConfigValue, "_", " "

  'If = in value, remove it and anything before it
  '(ie convert OSC = HS to HS)
  If InStr(ConfigValue, "=") <> 0 Then ConfigValue = Trim(Mid(ConfigValue, InStr(ConfigValue, ("=")) + 1))
  If InStr(Config, "=") <> 0 Then Config = Trim(Mid(Config, InStr(Config, ("=")) + 1))

  'Return -2 for best match
  If Config = ConfigValue Then Return -2


  'If value of setting in input config, value matches
  If WholeINSTR(Config, ConfigValue) = 2 Then Return -1
  If WholeINSTR(ConfigValue, Config) = 2 Then Return -1

  If Config = "DISABLED" And ConfigValue = "OFF" Then Return -1
  If Config = "ENABLED" And ConfigValue = "ON" Then Return -1

  'MCLR on some newer PICs
  If Config = "INTMCLR" And ConfigValue = "OFF" Then Return -1
  If Config = "EXTMCLR" And ConfigValue = "ON" Then Return -1

  'Internal oscillator
  If ConfigValue = "INT" Or ConfigValue = "INT IO" Then

    'Use non-clockout mode
    If WholeINSTR(Config, "CLKOUT") = 2 Then Return 0
    If WholeINSTR(Config, "IRCCLKOUT") = 2 Then Return 0

    'Possible matches
    If InStr(Config, "INTRC IO") <> 0 Then Return -1

    If InStr(Config, "INTRC OSC") <> 0 Then Return -1

    If Config = "INTRC" Then Return -1

    'If "INTOSC" found, make sure it's the option with IO
    If InStr(Config, "INTOSC") <> 0  Then
      If InStr(Config, "IO") <> 0 Then Return -1
      If MatchAny Then Return -1
      'This option doesn't have IO, return false if one with IO is found
      For CurrOption = 1 To COC
        ' Build 1285.  The Q20 chips have a CONFIG that has a substring of `IO` so added additional check to isolate  
        If InStr(ConfigOps(CurrOption).OP, "IO") <> 0 and  InStr(ConfigOps(CurrOption).OP, "VDDIO") = 0 Then
          Return 0
        End If
      Next
      Return -1
    End If

    If InStr(Config, "INTIO67") <> 0 Then Return -1
    If InStr(Config, "INTIO2") <> 0 Then Return -1
    If InStr(Config, "IRCIO67") <> 0 Then Return -1
    If InStr(Config, "IRC") <> 0 Then Return -1
    If InStr(Config, "HFINT32") <> 0 Then Return -1
    If InStr(Config, "HFINTOSC 1MHZ") <> 0 Then Return -1
    'Normally only want the highest to match, but if match any then match any internal oscillator
    If MatchAny Then
      If InStr(Config, "HFINT") <> 0 Then Return -1
      If InStr(Config, "LFINTOSC") <> 0 Then Return -1

      If InStr(Config, "LFINT") <> 0 Then Return -1

      If InStr(Config, "INTIO") <> 0 Then Return -1
    EndIf

  End If

  'No match found
  Return 0
  
  End Function

Sub CreateCallTree
  End Sub

Sub CreateReservedWordsList
  dim ReservedWordFile as string
  dim ReservedWordIn as string
  dim ReservedWordCounter as Integer
  dim As Byte ReservedwordLoop = 0

  'Detect GCBASIC install directory
  ID = ExePath
  If ID = "" Or ID = "." THEN
    ID = CURDIR
    #IFDEF __FB_UNIX__
      If Right(ID, 1) = "/" Then ID = Left(ID, Len(ID) - 1)
    #ELSE
      If Right(ID, 1) = "\" Then ID = Left(ID, Len(ID) - 1)
    #ENDIF
  End If

  'Read list
  #IFDEF __FB_UNIX__
    ReservedWordFile = ID + "/reservedwords.dat"
  #ELSE
    ReservedWordFile = ID + "\reservedwords.dat"
  #ENDIF
  IF Dir(ReservedWordFile) = "" THEN
    PRINT "Cannot find " + ReservedWordFile
    Print
    PRINT "GCBASIC cannot operate without this file"
    If PauseOnErr = 1 THEN
      PRINT
      PRINT "Press any key to continue"
      Sleep
    END IF
    END
  END IF

  ReservedWordCounter = 0
  OPEN ReservedWordFile FOR INPUT AS #1
  Do While NOT EOF(1)
      Line Input #1,  ReservedWordIn
      ReservedWords( ReservedWordCounter )  = ReservedWordIn
      ' print ReservedWords( ReservedWordCounter )
      ReservedWordCounter = ReservedWordCounter +1
      For ReservedwordLoop = 1 to len ( ReservedWordIn )
          If ReservedWordCounter > 1 then
            ReservedwordC = ReservedwordC + ASC( Mid(ReservedWordIn,ReservedwordLoop ,1) )
          End if
      Next 
  Loop
  CLOSE  #1
  If Val( Mid(ReservedWords( 0 ), Instr( ReservedWords( 0 )," ") ) ) + ReservedwordC <> 255 Then
      LogError "Invalid Reservedwords.dat - reinstall GCBASIC toolchain"
  End If
  ReservedwordC = Val( Mid(ReservedWords( 0 ), Instr( ReservedWords( 0 )," ") ) )       
  End Sub

Sub DisplayCallTree

  'Debugging sub
  'Will show all calls in program

  Dim CurrSub As Integer
  Dim CurrCall As LinkedListElement Pointer
  Dim CalledSub As SubType Pointer

  For CurrSub = 0 To SBC
    If Subroutine(CurrSub)->Required Then
      Print Subroutine(CurrSub)->Name

      CurrCall = Subroutine(CurrSub)->CallList->Next
      Do While CurrCall <> 0
        If CurrCall->MetaData <> 0 Then
          CalledSub = CurrCall->MetaData
          Print , CalledSub->Name
        End If

        CurrCall = CurrCall->Next
      Loop
      Print

    End If
  Next
  
  End Sub

Sub ExtAssembler
  Dim As String DataSource, ErrFile, Temp
  Dim As Integer PD, Result, compiledOK

  compiledOK = -1

  AsmExe = ReplaceToolVariables(AsmExe,,, AsmTool)
  AsmParams = ReplaceToolVariables(AsmParams, "asm",, AsmTool)

  PRINT
  'Assemble program with external assembler including MPASM
  Temp = Message("MakeExternalTool")
  Replace Temp, "%tool%", Trim(Str(AsmExe))
  PRINT Temp


  IF VBS = 1 THEN PRINT SPC(5); Message("Calling") + AsmExe
  Result = Exec(AsmExe, AsmParams)
  'SHELL MakeASM

  'Check that the assembly was successful
  If Result = -1 Then
    LogError Message("AssemblerNotFound")
    compiledOK = 0

  ELSEIF OPEN(Left(OFI, Instr(OFI, ".")) + "err" For Input As #1) = 0 THEN

    DO WHILE NOT EOF(1)
      LINE INPUT #1, DataSource
      IF INSTR(UCase(DataSource), "ERROR") <> 0 THEN
        LogError "ASM: " + Trim(DataSource)
        compiledOK = 0
      END IF
    LOOP
    CLOSE
  END IF

  IF VBS = 1 AND compiledOK = -1 then
    Print "Assembled successfully using "+AsmExe
  END IF

  'Get name of hex file to download
  HFI = OFI
  For PD = Len(HFI) To 1 Step -1
    If Mid(HFI, PD, 1) = "." Then
      HFI = Left(HFI, PD) + "hex"
      Exit For
    End If
  Next
  
  End Sub



Sub PICASAssembler
  'Call PIC-AS
  Dim As String DataSource, ErrFile, Temp
  Dim As Integer PD, Result, LBracePos, RBracePos
  Dim as string ErrorArray()

  AsmExe = ReplaceToolVariables(AsmExe,,, AsmTool)

  AsmParams = ReplaceToolVariables(AsmParams, "asm",, AsmTool)

  IF VBS = 1 THEN PRINT SPC(5); Message("Calling") + AsmExe

  Result = Shell( chr(34)+AsmExe+" "+AsmParams+" 2>&1"+chr(34))

  'Check that the assembly was successful
  If Result = -1 then
    Temp = Message("PICASFailtoLaunch")
    Replace Temp, "%var%", "Failed to Launch"
    LogError Temp

  elseif OPEN(ReplaceToolVariables("%filename%", "err")  For Input As #1) = 0 THEN
    DO WHILE NOT EOF(1)
      LINE INPUT #1, DataSource

      ' A warning that is really a serious error!!
      IF INSTR(UCase(DataSource), "WARNING:") <> 0 and INSTR(UCase(DataSource), "(1343)") <> 0 THEN
        'xcAshfc.4::: warning: (1343) hexfile data at address 0x1000 (0x08) overwritten with 0xF3

        'just format the output. Oh my... a real issue to sort this out!
        if instr( DataSource, "\\" ) > 0 then
          StringSplit ( DataSource, "\\",-1,ErrorArray() )
        else
          StringSplit ( DataSource, "\",-1,ErrorArray() )
        end if

        Temp  = mid ( ErrorArray( ubound(ErrorArray)), 2 )

        Replace( Temp, ":: WARNING", ": Error" )
        LBracePos = Instr ( Temp, "(" )-1
        RBracePos = Instr ( Temp, ")" )+2

        Temp = Left( Temp, LBracePos) + mid( temp, RBracePos, 255   )

        replace ( temp, ":", " (")
        replace ( temp, ":", "): ")

        ErrorsFound = -1
        LogOutputMessage Temp+" "

      END IF



      IF INSTR(UCase(DataSource), "ERROR") <> 0 THEN

        'Source:  10_use_pwm_via_ccp_and_adc_to_control_led_brightness.S:661:: error: (876) syntax error
        'or
        'Source:   D:\Great-Cow-BASIC-Demonstration-Sources.git\trunk\GLCD_Solutions\GLCD_Simple_Demonstration_Solutions\GLCD_Simple_Demonstration_Hardware_I2C_16f18855_for_SSD1306@32.o:172:: error: (1357) fixup overflow storing 0x825 in 2 bytes at 0xF98/0x2 -> 0x7CC (D:\Great-Cow-BASIC-Demonstration-Sources.git\trunk\GLCD_Solutions\GLCD_Simple_Demonstration_Solutions\GLCD_Simple_Demonstration_Hardware_I2C_16f18855_for_SSD1306@32.o 172/0x8)
        'or
        'Source:    C:\Users\admin\AppData\Local\Temp\xcAsdns.4::: warning: (1343) hexfile data at address 0x1000 (0x20) overwritten with 0xF3

        'Target:  10_use_pwm_via_ccp_and_adc_to_control_led_brightness.S (661):  Error: syntax error




        ErrorsFound = -1
        LogOutputMessage DataSource+" "

      END IF


      IF INSTR(UCase(DataSource), ucase("cannot find the path")) <> 0 THEN

        Temp = Message("PICASFailtoLaunch")
        Replace Temp, "%var%", DataSource
        LogError Temp

      END IF

    LOOP
    CLOSE

  END IF

  'Get name of hex file to download
  HFI = OFI
  For PD = Len(HFI) To 1 Step -1
    If Mid(HFI, PD, 1) = "." Then
      HFI = Left(HFI, PD) + "hex"
      Exit For
    End If
  Next
  
  End Sub

Sub ExtractParameters(ByRef NewSubCall As SubCallType, CalledSubName As String, CallParams As String, Origin As String)

  Dim As String TrimParams, SubSig, Temp
  Dim As Integer FP, PD
  Dim As Integer CurrPos, CurrLevel
  Dim As String CurrChar

  'Clear parameter list
  With NewSubCall
    .CalledID = 0
    .Params = 0
  End With

  'Get parameters
  'Find Sub calls with parameters in program
  TrimParams = CallParams
  SubSig = ""

  If CallParams = "" Then
    With NewSubCall
      .Params = 0
      .CallSig = ""
    End With
    Exit Sub

  Else
    With NewSubCall

      'Get Origin
      .Origin = Origin

      'Parse values - split at commas, except if commas are inside brackets
      CurrLevel = 0
      .Params = 1
      .Param(.Params, 1) = ""
      For CurrPos = 1 To Len(TrimParams)
        CurrChar = Mid(TrimParams, CurrPos, 1)
        If CurrChar = "(" Then
          CurrLevel += 1
        ElseIf CurrChar = ")" Then
          CurrLevel -= 1
        End If
        If CurrChar = "," And CurrLevel = 0 Then
          'Found comma that splits parameters
          .Param(.Params, 1) = Trim(.Param(.Params, 1))
          .Param(.Params, 2) = TypeOfValue(ReplaceFnNames(.Param(.Params, 1)), Subroutine(GetSubID(Origin)), -1)
          'Print .Param(.Params, 1) + " is a " + .Param(.Params, 2)
          SubSig += GetTypeLetter(.Param(.Params, 2))
          If .Params + 1 = MAXPARAMS Then

              If (( compilerdebug and cCOMPILEUPDATESUBMAP ) = cCOMPILEUPDATESUBMAP )  Then
                Print "256 CompileUpdateSubMap: Call to " + CalledSubName + " = maximum number of parameters exceeded " 
              End if
          
              temp = Message("MaxParametersExceeded")
              Replace temp, "%param%", str(MAXPARAMS)
              LogError temp, origin
              Exit For
          End If
          .Params += 1
          .Param(.Params, 1) = ""
        Else
          .Param(.Params, 1) = .Param(.Params, 1) + CurrChar
        End If
      Next

      'Get type of final parameter
      .Param(.Params, 1) = Trim(.Param(.Params, 1))
      .Param(.Params, 2) = TypeOfValue(ReplaceFnNames(.Param(.Params, 1)), Subroutine(GetSubID(Origin)), -1)
      'Print .Param(.Params, 1) + " is a " + .Param(.Params, 2)
      SubSig += GetTypeLetter(.Param(.Params, 2))

      'Print
      .CallSig = SubSig
    End With
  End If
  End Sub

Sub FinalOptimise

  'Replace call with rcall where possible (AVR and 18F)
  OptimiseCalls

  'Perform final optimisation on code, before writing assembly
  OptimiseIF

  'Check for uncompiled lines
  FindUncompiledLines
  
  End Sub

Sub FindAssembly (CompSub As SubType Pointer)
  Dim As String Temp, CalledSub
  Dim As Integer PD
  Dim As LinkedListElement Pointer CurrLine, LineElements, CurrElement
  Dim As AsmCommand Pointer CallPos, RCallPos, ThisCmdPos

  CallPos = IsASM("call")
  RCallPos = IsASM("rcall")

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    'If "ASM" is at start of line, remove it

    IF Left(CurrLine->Value, 4) = "ASM " THEN
      CurrLine->Value = Trim(Mid(CurrLine->Value, 5))
      'Allow use of ASM REM x for debugging
      If Left(CurrLine->Value, 10) = "SHOWDEBUG " Then
        Replace ( CurrLine->Value, "SHOWDEBUG ", "")
        CurrLine->Value = ";" + LCase(Trim(Mid(CurrLine->Value, 11)))
      Else
        CurrLine->Value = " " + LCase(Left(CurrLine->Value, LEN(Temp))) + Mid(CurrLine->Value, LEN(Temp) + 1)
      End If
      IF INSTR(CurrLine->Value, ";?") <> 0 THEN CurrLine->Value = RTrim(Left(CurrLine->Value, INSTR(CurrLine->Value, ";?") - 1))
    Else
      ThisCmdPos = IsASM(CurrLine->Value)
      If ThisCmdPos <> 0 AND Left(CurrLine->Value, 1) <> " " THEN
        Temp = Trim(ThisCmdPos->Syntax)
        IF InStr(Temp, " ") <> 0 THEN Temp = Trim(Left(Temp, INSTR(Temp, " ") - 1))

        'Find variables
        LineElements = GetElements(CurrLine->Value)
        CurrElement = LineElements->Next
        If CurrElement <> 0 Then CurrElement = CurrElement->Next 'Don't try adding command as variable
        Do While CurrElement <> 0
          RequestVariable(CurrElement->Value, CompSub)
          CurrElement = CurrElement->Next
        Loop
        LinkedListDeleteList(LineElements, 0)

        'Record calls
        If ThisCmdPos = CallPos Or ThisCmdPos = RCallPos Then
          CalledSub = Trim(Mid(CurrLine->Value, LEN(Temp) + 1))
          IF InStr(CalledSub, ";?") <> 0 THEN CalledSub = RTrim(Left(CalledSub, INSTR(CalledSub, ";?") - 1))
          RequestSub(CompSub, CalledSub, "")
        End If

        CurrLine->Value = " " + LCase(Left(CurrLine->Value, LEN(Temp))) + Mid(CurrLine->Value, LEN(Temp) + 1)

        IF InStr(CurrLine->Value, ";?") <> 0 THEN CurrLine->Value = RTrim(Left(CurrLine->Value, INSTR(CurrLine->Value, ";?") - 1))
      End If
    End If

    CurrLine = CurrLine->Next
  Loop
  End Sub

Function FindPotentialBanks(CurrLine As LinkedListElement Pointer, OutList As LinkedListElement Pointer, CheckedLines As LinkedListElement Pointer) As LinkedListElement Pointer
  'Will accept a code line and an optional output list
  'Will check to see what bank line will use, and add it to the list
  'Need to keep list of lines that have been checked to avoid infinite loops

  Dim As Integer CreatedCheckedList, LineNotChecked, BankIsNew
  Dim As LinkedListElement Pointer CurrPrev, CurrPrevPos, CheckListPos, OutListPos
  Dim As ProgLineMeta Pointer CurrMeta, CheckMeta

  'Make sure output list exists
  If OutList = 0 Then
    OutList = LinkedListCreate
  End If

  'Make sure check lines list exists
  CreatedCheckedList = 0
  If CheckedLines = 0 Then
    CreatedCheckedList = -1
    CheckedLines = LinkedListCreate
  End If

  'Check to see if line requires a bank
  CurrMeta = CurrLine->MetaData
  If CurrMeta <> 0 Then

    'Check all previous lines to see if they set a bank
    'MetaData in PrevCommands points to line
    'If MetaData in PrevCommands points to 0, line is first in sub
    CurrPrevPos = CurrMeta->PrevCommands->Next
    Do While CurrPrevPos <> 0

      CurrPrev = CurrPrevPos->MetaData

      'Is first line in sub?
      If CurrPrev = 0 Then
        'If so, it will always be in bank 0
        BankIsNew = -1
        OutListPos = OutList->Next
        Do While OutListPos <> 0
          If OutListPos->NumVal = 0 Then
            BankIsNew = 0
            Exit Do
          End If
          OutListPos = OutListPos->Next
        Loop
        If BankIsNew Then
          OutListPos = LinkedListInsert(OutList, "")
          OutListPos->NumVal = 0
        End If

      Else
        'Has line been checked already?
        LineNotChecked = -1
        CheckListPos = CheckedLines->Next
        Do While CheckListPos <> 0
          If CheckListPos->MetaData = CurrPrev Then
            LineNotChecked = 0
            Exit Do
          End If
          CheckListPos = CheckListPos->Next
        Loop
        'Line not checked, so check it and record the check
        If LineNotChecked Then
          LinkedListInsert(CheckedLines, CurrPrev)

          CheckMeta = CurrPrev->MetaData
          If CheckMeta <> 0 Then
            If CheckMeta->RequiredBank <> -1 Then
              'Have found a bank, add to the bank list
              BankIsNew = -1
              OutListPos = OutList->Next
              Do While OutListPos <> 0
                If OutListPos->NumVal = CheckMeta->RequiredBank Then
                  BankIsNew = 0
                  Exit Do
                End If
                OutListPos = OutListPos->Next
              Loop
              If BankIsNew Then
                OutListPos = LinkedListInsert(OutList, "")
                OutListPos->NumVal = CheckMeta->RequiredBank
              End If

            Else
              'Check lines before current previous line
              OutList = FindPotentialBanks(CurrPrev, OutList, CheckedLines)

            End If
          End If
        End If

      End If

      CurrPrevPos = CurrPrevPos->Next
    Loop

  End If

  'Delete checked lines list if created by this sub
  If CreatedCheckedList Then
    LinkedListDelete(CheckedLines)
  End If

  Return OutList
  End Function

Sub FindUncompiledLines
  'Search the compiler output listing for uncompiled lines
  'Show error where found

  Dim As LinkedListElement Pointer CurrLine
  Dim As String TempLine, Origin, temp

  CurrLine = CompilerOutput->CodeList->Next
  Do While CurrLine <> 0
    IF INSTR(CurrLine->Value, ";?F") <> 0 THEN
      Origin = Mid(CurrLine->Value, INSTR(CurrLine->Value, ";?F"))
      TempLine = Trim(Left(CurrLine->Value, InStr(CurrLine->Value, ";?F") - 1))
      'Recognise some block ends
      'These are typically dealt with when the start is found
      'If left in, there's a missing start somewhere!
      If TempLine = "END REPEAT" Then
        LogError Message("EndWithoutRepeat"), Origin
      ElseIf TempLine = "LOOP" Or Left(TempLine, 5) = "LOOP " Then
        LogError Message("LoopWithoutDo"), Origin
      ElseIf TempLine = "ELSE" Then
        LogError Message("ElseWithoutIf"), Origin
      ElseIf TempLine = "NEXT" Then
        LogError Message("NextWithoutFor"), Origin

      'Anything else uncompiled at this point is a syntax error or RAISECOMPILERERROR
      Else
        If instr(ucase(TempLine), "RAISECOMPILERERROR") > 0 Then
            replace TempLine, "RAISECOMPILERERROR" , ""
            tempLine = GetString ( trim(tempLine) )
            If Instr( tempLine , "%") > 0 Then
              ReplaceAll tempLine, "%", ""
              tempLine = Message ( tempLine )
            End If
            temp = mid(TempLine,1 + InStr(TempLine,chr(34)))  'search for first double quote
            temp = Mid(temp,1,InStr(temp,chr(34))-1)            'search for next double quote
            LogError temp, Origin                                    'change message to desired
        Else
          LogError Message("SynErr"), Origin
        End if
      End If
    END If



    CurrLine = CurrLine->Next
  Loop
  End Sub

Function FixBit (InBit As String, Origin As String) As String
  'Fix any references to bits > 7

  Dim As String DivChar, VarName, VarType, VarBit, Temp
  Dim As Integer B
  Dim As VariableType Pointer FoundUserVar

  'Get dividing character
  DivChar = "."
  IF INSTR(InBit, ".") = 0 AND INSTR(InBit, ",") <> 0 Then DivChar = ","

  'Deal with bit variables (which are all stored in main)
  If InStr(InBit, DivChar) = 0 Then
    FoundUserVar = HashMapGet(@(Subroutine(0)->Variables), InBit)
    If FoundUserVar <> 0 Then
      InBit = UCASE(FoundUserVar->BitVarLocation)
    End If
  End If

  'Exit if not a bit
  IF INSTR(InBit, DivChar) = 0 Then Return Inbit

  VarName = Trim(Left(InBit, INSTR(InBit, DivChar) - 1))
  VarBit = Trim(Mid(InBit, INSTR(InBit, DivChar) + 1))
  VarType = TypeOfVar(VarName, Subroutine(GetSubID(Origin)))
  B = VAL(VarBit)

  'If bit < 8, return without change
  If B < 8 Then Return InBit


  'Prepare error
  Temp = Message("BadVarBit")
  Replace Temp, "%var%", VarName
  Replace Temp, "%type%", VarType
  Replace Temp, "%bit%", VarBit

  'Is bit number valid?
  If B < 8 * GetTypeSize(VarType) Then
    Return GetByte(VarName, B \ 8) + DivChar + Trim(Str(B Mod 8))
  Else
    If ChipAVRDX Then
      If Instr( Ucase(Temp), "_OUT") > 0 then Replace Temp, "_OUT", ""
      If Instr( Ucase(Temp), "_IN") > 0 then Replace Temp, "_IN", ""
    End If
    If Instr( Ucase(Temp), "%") > 0 then Replace Temp, "%", ""
    LogError Temp, Origin
  End If

  'Return unchanged
  Return InBit
  
  End Function

Sub FixFunctions(CompSub As SubType Pointer)

  'Fix functions that are used by DO and WAIT, so they are updated inside
  'the loops
  Dim As String SubName
  Dim As Integer T, CD
  Dim As LinkedListElement Pointer CurrLine, FindStart

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0

    If Left(CurrLine->Value, 9) = " call FN_" THEN
      IF Left(CurrLine->Next->Value, 11) = "SysWaitLoop" OR Left(CurrLine->Next->Value, 11) = "SysDoLoop_S" THEN
        SubName = Mid(CurrLine->Value, 10)
        FindStart = CurrLine->Prev
        Do While FindStart <> 0

          IF FindStart->Value = ";FNSTART," + SubName THEN

            'Maintain the IsLabel as the LinkedListDelete is moving the code
            GetMetaData(FindStart)->IsLabel = GetMetaData(CurrLine->Next)->IsLabel

            FindStart->Value = CurrLine->Next->Value
            LinkedListDelete(CurrLine->Next)
            CurrLine = FindStart->Prev
            Exit Do
          END If

          FindStart = FindStart->Prev
        Loop
      'Not inside a loop, no change needed, just delete the ;FNSTART
      Else
        SubName = Mid(CurrLine->Value, 10)
        FindStart = CurrLine->Prev
        Do While FindStart <> 0
          IF FindStart->Value = ";FNSTART," + SubName THEN
            LinkedListDelete(FindStart)
            Exit Do
          END If
          FindStart = FindStart->Prev
        Loop
      END If

    ElseIf Left(CurrLine->Value, 10) = ";BLOCKEND," THEN
      IF Left(CurrLine->Next->Value, 11) = "SysWaitLoop" OR Left(CurrLine->Next->Value, 11) = "SysDoLoop_S" THEN
        SubName = Mid(CurrLine->Value, 11)
        FindStart = CurrLine->Prev
        Do While FindStart <> 0

          IF FindStart->Value = ";BLOCKSTART," + SubName THEN
            FindStart->Value = CurrLine->Next->Value

             'Maintain the IsLabel as this routine is moving the label about
            GetMetaData(FindStart)->IsLabel = GetMetaData(CurrLine->Next)->IsLabel

            LinkedListDelete(CurrLine->Next)
            CurrLine = LinkedListDelete(CurrLine)
            Exit Do
          END IF
          FindStart = FindStart->Prev
        Loop
      END IF

    END IF
    CurrLine = CurrLine->Next
  Loop

  
  End Sub

Sub FixPointerOps (CompSub As SubType Pointer)

  'AVR Only

  'Replace operations on the SysPointerX pseudo variable with operations
  'using ld X/st x.

  Dim As String DataSource, DataReg
  Dim As LinkedListElement Pointer CurrLine

  Dim As Integer AccessType

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    If WholeINSTR(CurrLine->Value, "SysPointerX") > 0 Then

      AccessType = 0
      If WholeINSTR(CurrLine->Value, "SysPointerX") = 2 Then
        AccessType = 1
      ElseIf WholeINSTR(CurrLine->Value, "SysPointerXInc") = 2 Then
        AccessType = 2
      End If

      If AccessType > 0 Then
        DataSource = CurrLine->Value

        Select Case LCase(Left(Trim(DataSource), 3))
          Case "lds":
          DataReg = Mid(Trim(DataSource), 4)
          DataReg = Trim(Left(DataReg, Instr(DataReg, ",") - 1))
          If AccessType = 1 Then
            CurrLine->Value = " ld " + DataReg + ",X"
          Else
            CurrLine->Value = " ld " + DataReg + ",X+"
          End If


          Case "sts":
          DataReg = Trim(Mid(DataSource, Instr(DataSource, ",") + 1))
          If AccessType = 1 Then
            CurrLine->Value = " st X," + DataReg
          Else
            CurrLine->Value = " st X+," + DataReg
          End If

          Case Else:
          'If an error message has already happened then we dont want to issue this message
          If ErrorsFound <> -1 then
            LogError "Internal error in FixPointerOps"
          End if

        End Select
      End If
    End If

    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub FixSinglePinSet
  'Deal with pin direction setting on 12 bit PIC
  'Need to cache W into TRISIO before tris
  'Also need to copy TRISIO into tris after bcf/bsf on TRISIO
  'Need to operate on every subroutine

  'Also need to fix OPTION in the same way

  Dim As Integer CurrSub, NextSamePort, SearchPos, FoundPos, AddedShadows
  Dim As LinkedListElement Pointer CurrLine
  Dim As String Port, NextPort, TrisPort
  Dim As SubType Pointer InitSys
  AddedShadows = 0

  '12 bit PIC only
  If ChipFamily <> 12 Then Exit Sub

  'Only needed if individual pin dir is set
  If PinDirShadows = 0 Then Exit Sub

  'Check each sub
  For CurrSub = 0 To SBC
    With *Subroutine(CurrSub)
      If .Required And .Compiled Then

        'Check each line of sub
        CurrLine = .CodeStart->Next
        Do While CurrLine <> 0

          'Find tris x
          If Left(CurrLine->Value, 6) = " tris " Then
            'Get port
            Port = Trim(Mid(CurrLine->Value, 7))

            'Get tris cache
            TrisPort = ""
            If Port = "GPIO" Then
              TrisPort = "IO"
            ElseIf Left(Port, 4) = "PORT" And Len(Port) = 5 Then
              TrisPort = Mid(Port, 5)
            End If

            'Check if shadow needed
            FoundPos = -1
            For SearchPos = 1 To PinDirShadows
              If PinDirShadow(SearchPos) = TrisPort Then
                FoundPos = SearchPos
                Exit For
              End If
            Next

            If FoundPos <> -1 Then
              'Set buffer
              LinkedListInsert(CurrLine->Prev, " movwf TRIS" + TrisPort)

              'Add caching instructions
              AddVar("TRIS" + TrisPort, "BYTE", 1, 0, "REAL", "", , -1)
            End If

          'Find option
          ElseIf Trim(CurrLine->Value) = "option" Then

            'Check if shadow needed
            FoundPos = -1
            For SearchPos = 1 To PinDirShadows
              If PinDirShadow(SearchPos) = "OPTION_REG" Then
                FoundPos = SearchPos
                Exit For
              End If
            Next

            If FoundPos <> -1 Then
              'Set buffer
              LinkedListInsert(CurrLine->Prev, " movwf OPTION_REG")

              'Add caching instructions
              AddVar("OPTION_REG", "BYTE", 1, 0, "REAL", "", , -1)
            End If

          'Find bcf/bsf OPTION_REG
          ElseIf Left(CurrLine->Value, 16) = " bsf OPTION_REG," Or Left(CurrLine->Value, 16) = " bcf OPTION_REG," Then
            'Print "Found single dir set:", CurrLine->Value

            'If there is a block of dir commands, get last one for same port
            Do
              NextSamePort = 0

              'Is there another line?
              If CurrLine->Next <> 0 Then
                'Is the next line an option setting instruction?
                If Left(CurrLine->Next->Value, 16) = " bsf OPTION_REG," Or Left(CurrLine->Next->Value, 16) = " bcf OPTION_REG," Then
                  CurrLine = CurrLine->Next
                  NextSamePort = -1
                End If
              End If
            Loop While NextSamePort

            'Add caching instructions
            AddVar("OPTION_REG", "BYTE", 1, 0, "REAL", "", , -1)
            CurrLine = LinkedListInsert(CurrLine, " movf OPTION_REG,W")
            CurrLine = LinkedListInsert(CurrLine, " option")

          'Find bcf/bsf TRISx
          ElseIf Left(CurrLine->Value, 9) = " bsf TRIS" Or Left(CurrLine->Value, 9) = " bcf TRIS" Then
            'Print "Found single dir set:", CurrLine->Value

            'Get port
            Port = Mid(CurrLine->Value, 10)
            If InStr(Port, ",") <> 0 Then Port = Left(Port, InStr(Port, ",") - 1)
            Port = Trim(Port)

            'If there is a block of dir commands, get last one for same port
            Do
              NextSamePort = 0

              'Is there another line?
              If CurrLine->Next <> 0 Then
                'Is the next line a tris setting instruction?
                If Left(CurrLine->Next->Value, 9) = " bsf TRIS" Or Left(CurrLine->Next->Value, 9) = " bcf TRIS" Then
                  'What port does it set?
                  NextPort = Mid(CurrLine->Value, 10)
                  If InStr(NextPort, ",") <> 0 Then NextPort = Left(NextPort, InStr(NextPort, ",") - 1)
                  NextPort = Trim(NextPort)
                  'Is it the same?
                  If NextPort = Port Then
                    CurrLine = CurrLine->Next
                    NextSamePort = -1
                  End If
                End If
              End If
            Loop While NextSamePort

            If Len(Port) = 1 Or Port = "IO" Then
              'Calculate port name
              If Len(Port) = 1 Then
                TrisPort = "PORT" + Port
              Else
                TrisPort = "GPIO"
              End If

              'Add caching instructions
              AddVar("TRIS" + Port, "BYTE", 1, 0, "REAL", "", , -1)
              CurrLine = LinkedListInsert(CurrLine, " movf TRIS" + Port + ",W")
              CurrLine = LinkedListInsert(CurrLine, " tris " + TrisPort)

            End If

          End If

          CurrLine = CurrLine->Next
        Loop

      End If
    End With
  Next

  'Add pin dir shadow init code to end of InitSys
  If PinDirShadows > 0 Then
    SearchPos = LocationOfSub("InitSys", "")
    If SearchPos > 0 Then
      InitSys = Subroutine(SearchPos)
      If InitSys <> 0 Then

        'Get last line of sub
        CurrLine = InitSys->CodeStart
        Do While CurrLine->Next <> 0
          CurrLine = CurrLine->Next
        Loop

        'Add buffer setting code
        For SearchPos = 1 To PinDirShadows
          If PinDirShadow(SearchPos) <> "OPTION_REG" Then
            If AddedShadows = 0 Then
              CurrLine = LinkedListInsert(CurrLine, " movlw 255")
            End If
            AddedShadows += 1
            CurrLine = LinkedListInsert(CurrLine, " movwf TRIS" + PinDirShadow(SearchPos))
          End If
        Next

      End If
    End If
  End If
  
  End Sub

Sub FixTemporaryVariables
  'Find any temporary variables that will be overwritten by subroutines, and rename to prevent
  Dim As LinkedListElement Pointer CurrVar, CheckVar, CallerList, CallerListPos, CalledListPos
  Dim As LinkedListElement Pointer UsedVarPos, VisitList, VisitListPos, SearchPos, CheckNewPos
  Dim As LinkedListElement Pointer CurrLine
  Dim As String CurrVarName, NewVarName
  Dim As SubType Pointer CurrSub, CallingSub
  Dim As Integer CurrSubID, KeepSearching, SearchIn
  Dim As HashMap Pointer UsedTempVars
  Dim As VariableType Pointer OldVar

  'Remove duplicate names from list
  For CurrSubID = 0 To SBC
    CurrSub = Subroutine(CurrSubID)
    If CurrSub->Required Then
      CurrVar = CurrSub->TemporaryVars
      Do While CurrVar <> 0
        CurrVarName = UCase(CurrVar->Value)
        CurrVar->Value = CurrVarName
        CheckVar = CurrVar->Next
        Do While CheckVar <> 0
          If CurrVarName = UCase(CheckVar->Value) Then
            CheckVar = LinkedListDelete(CheckVar)
          End If
          CheckVar = CheckVar->Next
        Loop

        CurrVar = CurrVar->Next
      Loop
    End If
  Next

  'Search for conflicts
  'Basic algorithm:
  'Create visit list, fill with main Sub and everything called from it
  'For each item on visit list
  ' - Check that it does not steal any variables from its callers
  ' - Add all subroutines called to visit list
  ' - Subroutines may be visited multiple times if needed
  VisitList = LinkedListCreate
  VisitListPos = VisitList
  VisitListPos = LinkedListInsert(VisitListPos, Subroutine(0))
  Do While VisitListPos <> 0
    CurrSub = VisitListPos->MetaData

    'Check that this Sub does not interfere with callers
    'Get all subroutines that call current sub, build hash map of used temporary vars
    UsedTempVars = HashMapCreate
    CallerList = GetCalledSubs(CurrSub, , -1)
    CallerListPos = CallerList->Next
    Do While CallerListPos <> 0
      CallingSub = CallerListPos->MetaData
      CurrVar = CallingSub->TemporaryVars->Next
      Do While CurrVar <> 0
        HashMapSet(UsedTempVars, CurrVar->Value, CallingSub->Name, -1)
        CurrVar = CurrVar->Next
      Loop
      CallerListPos = CallerListPos->Next
    Loop

    'Check if any temp variables in this Sub are used by callers
    CheckVar = CurrSub->TemporaryVars->Next
    Do While CheckVar <> 0
      If HashMapGetStr(UsedTempVars, CheckVar->Value) <> "" Then
        'Found problem variable
        'Decide new temp variable name
        NewVarName = CheckVar->Value
        Do
          KeepSearching = 0
          'Get name to try
          NewVarName = GetNextTempVar(NewVarName)

          'Check for conflict in this sub
          If LinkedListFind(CurrSub->TemporaryVars, NewVarName) <> 0 Then
            'Name won't work
            KeepSearching = -1
            GoTo TryNextName
          End If

          'Check for conflict with callers
          If HashMapGetStr(UsedTempVars, NewVarName) <> "" Then
            KeepSearching = -1
            GoTo TryNextName
          End If

          'If we got here, new name is valid

          TryNextName:
        Loop While KeepSearching

        'Clone variable to new name
        OldVar = HashMapGet(@(CurrSub->Variables), CheckVar->Value)
        If OldVar <> 0 Then
          'Local variable
          If OldVar->Type = "STRING" Then
            AddVar(NewVarName, "STRING*" + Str(OldVar->Size), OldVar->Size, CurrSub, OldVar->Pointer, "", OldVar->FixedLocation, -1, OldVar->Used)
          Else
            AddVar(NewVarName, OldVar->Type, OldVar->Size, CurrSub, OldVar->Pointer, "", OldVar->FixedLocation, -1, OldVar->Used)
          End If
        Else
          'Must be global
          OldVar = HashMapGet(@(Subroutine(0)->Variables), CheckVar->Value)
          If OldVar <> 0 Then
            If OldVar->Type = "STRING" Then
              AddVar(NewVarName, "STRING*" + Str(OldVar->Size), OldVar->Size, 0, OldVar->Pointer, "", OldVar->FixedLocation, -1, OldVar->Used)
            Else
              AddVar(NewVarName, OldVar->Type, OldVar->Size, 0, OldVar->Pointer, "", OldVar->FixedLocation, -1, OldVar->Used)
            End If
          End If
        End If

        'Change code to use new variable
        CurrLine = CurrSub->CodeStart->Next
        Do While CurrLine <> 0
          WholeReplace(CurrLine->Value, CheckVar->Value, NewVarName)
          CurrLine = CurrLine->Next
        Loop

        'Record use of new temporary variable
        CheckVar = LinkedListInsert(CheckVar, NewVarName)
      End If
      CheckVar = CheckVar->Next
    Loop

    'Add called subs to visit list
    CalledListPos = CurrSub->CallList->Next
    Do While CalledListPos <> 0
      'Search visit list, add if not already on list after current sub
      SearchPos = VisitListPos->Next
      If SearchPos = 0 Then
        LinkedListInsert(VisitListPos, CalledListPos->MetaData)
      Else
        Do While SearchPos <> 0
          If SearchPos->MetaData = CalledListPos->MetaData Then Exit Do
          If SearchPos->Next = 0 Then
            'Called Sub is not later in visit list already, so add
            '(Unless a called Sub calls this one, then we have recursion and adding will lock up compiler)
            If LinkedListFind(CallerList, CurrSub) = 0 Then
              LinkedListInsert(SearchPos, CalledListPos->MetaData)
            End If
          End If
          SearchPos = SearchPos->Next
        Loop
      End If

      CalledListPos = CalledListPos->Next
    Loop

    'Delete caller list
    HashMapDestroy(UsedTempVars)
    LinkedListDelete(CallerList, 0)

    VisitListPos = VisitListPos->Next
  Loop
  
  End Sub

Sub FreeCalcVar (VarName As String)
  'Mark a calc var as available for reuse
  Dim As String Temp
  Dim As Integer CV, CD

  If VarName <> "" Then
    'If name specified, only mark that var

    'Exit if input is not a calc var
    IF Left(UCase(VarName), 7) <> "SYSTEMP" Then Exit Sub

    Temp = Mid(Trim(VarName), 8)
    CV = VAL(Temp)

    CalcVars(CV).Status = "A"
  Else
    'If no name, mark all
    For CD = 1 to TCVC
      CalcVars(CD).Status = "A"
    Next
  End If
  End Sub

Function GenerateArrayPointerSet(DestVar As String, DestPtr As Integer, CurrSub As SubType Pointer, Origin As String) As LinkedListElement Pointer

  'DestPtr:
  ' 0: SysStringA on 16F, FSR0 on enhanced 16F/18F
  ' 1: SysStringB on 16F, FSR1 on enhanced 16F/18F
  '-1: FSR on 16F, FSR0 on 18F

  Dim As String ArrayHandler, Temp, ArrayName, WholePtrVar, DestPtrL, DestPtrH
  Dim As Integer ArrayType
  Dim As VariableType Pointer ArrayPtr

  'Create output list
  Dim As LinkedListElement Pointer CurrLine, OutList
  OutList = LinkedListCreate
  CurrLine = OutList

  'Get array name (no ( or $ at the end)
  ArrayName = Trim(UCase(DestVar))
  If INSTR(ArrayName, "(") <> 0 Then ArrayName = RTrim(Left(ArrayName, INSTR(ArrayName, "(") - 1))
  If INSTR(ArrayName, "$") <> 0 Then ArrayName = RTrim(Left(ArrayName, INSTR(ArrayName, "$") - 1))

  'Get array data, exit if not declared
  ArrayPtr = VarAddress(DestVar, CurrSub)
  IF ArrayPtr = 0 THEN
    Temp = Message("UndeclaredArray")
    Replace Temp, "%array%", ArrayName
    LogError Temp, Origin
    Return OutList
  END IF

  IF ArrayPtr = Cast( VariableType Pointer, INVALIDARRAYVALUE ) THEN
    Temp = Message("CannotHandleConstruction")
    Replace Temp, "%var%", DestVar
    Replace Temp, "%sub%", CurrSub->Name    
    LogError Temp, Origin
    Return OutList
  END IF

  'Find array type, get source location
  IF ArrayPtr->Pointer = "REAL" THEN
    ArrayPtr->Used = -1
    ArrayHandler = ArrayPtr->Name
    ArrayType = 0
  ElseIF ArrayPtr->Pointer = "POINTER" THEN
    ArrayHandler = "Sys" + ArrayName + "Handler"
    AddVar ArrayHandler, "WORD", 1, CurrSub, "REAL", Origin, , -1
    ArrayType = 1
  Else
    Print "Unknown array type, "; ArrayPtr->Name; " is "; ArrayPtr->Pointer
  END IF

  'Get dest pointer
  If ModePIC Then
    If ChipFamily = 15 Or ChipFamily = 16 Then
      If DestPtr = -1 Then DestPtr = 0
      DestPtrL = "FSR" + Str(DestPtr) + "L"
      DestPtrH = "FSR" + Str(DestPtr) + "H"
    Else
      If DestPtr = -1 Then
        WholePtrVar = "FSR"
      ElseIf DestPtr = 0 Then
        WholePtrVar = "SysStringA"
      ElseIf DestPtr = 1 Then
        WholePtrVar = "SysStringB"
      Else
        WholePtrVar = "SysStringC"
      End If
      AddVar WholePtrVar, "WORD", 1, 0, "REAL", Origin, , -1 'Needs to be global
      DestPtrL = GetByte(WholePtrVar, 0)
      DestPtrH = GetByte(WholePtrVar, 1)
    End If
  ElseIf ModeAVR Then
    If DestPtr = 0 Then
      WholePtrVar = "SysStringA"
    ElseIf DestPtr = 1 Then
      WholePtrVar = "SysStringB"
    Else
      WholePtrVar = "SysStringA"
    End If
    AddVar WholePtrVar, "WORD", 1, 0, "REAL", Origin, , -1 'Needs to be global
    DestPtrL = GetByte(WholePtrVar, 0)
    DestPtrH = GetByte(WholePtrVar, 1)
  ElseIf ModeZ8 Then

  End If

  If ModePIC Then
    'Warn about unsafe strings
    If ChipFamily = 12 Then
      LogWarning Message("WarningUnsafeString")
    End If

    If ChipFamily = 12 Or ChipFamily = 14 Then
      'DestPtr = -1: Set FSR directly
      If DestPtr = -1 Then
        'ArrayType = 0: Have a constant address
        If ArrayType = 0 Then
          CurrLine = LinkedListInsert(CurrLine, " movlw low " + ArrayHandler)
          CurrLine = LinkedListInsert(CurrLine, " movwf FSR")
          If HighFSR Then CurrLine = LinkedListInsert(CurrLine, " bankisel " + ArrayHandler)
        Else
          'ArrayType = 1: Have another pointer
          CurrLine = LinkedListInsert(CurrLine, " movf " + GetByte(ArrayHandler, 0) + ",W")
          CurrLine = LinkedListInsert(CurrLine, " movwf FSR")
          If HighFSR Then
            CurrLine = LinkedListInsert(CurrLine, " bcf STATUS, IRP")
            CurrLine = LinkedListInsert(CurrLine, " btfsc " + GetByte(ArrayHandler, 1) + ",0")
            CurrLine = LinkedListInsert(CurrLine, " bsf STATUS, IRP")
          End If
        End If
      Else
        If ArrayType = 0 Then
          CurrLine = LinkedListInsert(CurrLine, " movlw low " + ArrayHandler)
          CurrLine = LinkedListInsert(CurrLine, " movwf " + DestPtrL)
          CurrLine = LinkedListInsert(CurrLine, " movlw high " + ArrayHandler)
          CurrLine = LinkedListInsert(CurrLine, " movwf " + DestPtrH)
        Else
          CurrLine = LinkedListInsert(CurrLine, " movf " + GetByte(ArrayHandler, 0) + ",W")
          CurrLine = LinkedListInsert(CurrLine, " movwf " + DestPtrL)
          CurrLine = LinkedListInsert(CurrLine, " movf " + GetByte(ArrayHandler, 1) + ",W")
          CurrLine = LinkedListInsert(CurrLine, " movwf " + DestPtrH)
        End If
      End If

    ElseIf ChipFamily = 15 Then
      If ArrayType = 0 Then
        CurrLine = LinkedListInsert(CurrLine, " movlw low " + ArrayHandler)
        CurrLine = LinkedListInsert(CurrLine, " movwf " + DestPtrL)
        CurrLine = LinkedListInsert(CurrLine, " movlw high " + ArrayHandler)
        CurrLine = LinkedListInsert(CurrLine, " movwf " + DestPtrH)
      Else
        CurrLine = LinkedListInsert(CurrLine, " movf " + GetByte(ArrayHandler, 0) + ",W")
        CurrLine = LinkedListInsert(CurrLine, " movwf " + DestPtrL)
        CurrLine = LinkedListInsert(CurrLine, " movf " + GetByte(ArrayHandler, 1) + ",W")
        CurrLine = LinkedListInsert(CurrLine, " movwf " + DestPtrH)
      End If

    ElseIf ChipFamily = 16 Then
      If ArrayType = 0 Then
        CurrLine = LinkedListInsert(CurrLine, " lfsr " + Str(DestPtr) + "," + ArrayHandler)
      Else
        CurrLine = LinkedListInsert(CurrLine, " movff " + GetByte(ArrayHandler, 0) + "," + DestPtrL)
        CurrLine = LinkedListInsert(CurrLine, " movff " + GetByte(ArrayHandler, 1) + "," + DestPtrH)
      End If
    End If

  ElseIf ModeAVR Then
    If ArrayType = 0 Then
      CurrLine = LinkedListInsert(CurrLine, " ldi " + DestPtrL + ",low(" + ArrayHandler + ")")
      CurrLine = LinkedListInsert(CurrLine, " ldi " + DestPtrH + ",high(" + ArrayHandler + ")")
    ElseIf ArrayType = 1 Then
      CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(ArrayHandler, WholePtrVar, Origin))
    End If
  End If

  Return OutList
  End Function

Function GenerateAutoPinDir As LinkedListElement Pointer
  'Generates automatic pin direction setting code
  'Returns a linked list containing the Dir commands

  Dim As LinkedListElement Pointer OutList, CurrLine, CurrPin, SearchLine
  Dim As PinDirType Pointer CurrPinDir, CurrPortDir
  Dim As String WholePortName, Temp

  'Create output list
  OutList = LinkedListCreate
  CurrLine = OutList

  'Get pin directions/
  If (( compilerdebug and cGenerateAutoPinDir ) = cGenerateAutoPinDir ) Then
    Print "16: AUTOPINDIR","Pin", "In", "Out", "Read", "Written"
  End If
  CurrPin = PinDirections->Next
  Do While CurrPin <> 0
    CurrPinDir = CurrPin->MetaData
    If (( compilerdebug and cGenerateAutoPinDir ) = cGenerateAutoPinDir ) Then
      Print ,,CurrPin->Value, CurrPinDir->SetIn, CurrPinDir->SetOut, CurrPinDir->ReadFrom, CurrPinDir->WrittenTo
    End If
    'If direction not set manually, and required direction is known, set direction
    With (*CurrPinDir)
      'Checking a pin
      If InStr(CurrPin->Value, ".") <> 0 Then

        'If pin direction hasn't been set but is obvious, set it automatically
        If Not .SetIn And Not .SetOut Then

          'Make sure no conflicting whole port direction set used
          WholePortName = Left(CurrPin->Value, InStr(CurrPin->Value, ".") - 1)
          SearchLine = PinDirections->Next
          Do While SearchLine <> 0
            If SearchLine->Value = WholePortName Then
              'Have found entire port setting
              'If pin and port setting conflict, don't set
              CurrPortDir = SearchLine->MetaData
              'If whole port dir set, don't set automatically
              If CurrPortDir->WholePort <> 0 Then GoTo SkipPinDirSet
              'If whole port set at all, don't set automatically
              If CurrPortDir->SetIn Or CurrPortDir->SetOut Then GoTo SkipPinDirSet
              'If whole port dir clashes with single bit dir, don't set
              If Not (CurrPortDir->ReadFrom Or .ReadFrom) Xor (CurrPortDir->WrittenTo Or .WrittenTo) Then
                'Pin directions not set, and cannot be guessed - show warning
                If .ReadFrom And .WrittenTo Then
                  Temp = Message("WarningNoAutoPinDir")
                  Replace Temp, "%pin%", CurrPin->Value
                  LogWarning(Temp)
                End If
                GoTo SkipPinDirSet
              End If

              'No conflict, so stop looking for conflict and set dir if needed
              Exit Do
            End If
            SearchLine = SearchLine->Next
          Loop

          'Pin is read only
          If .ReadFrom Then
            CurrLine = LinkedListInsert(CurrLine, "DIR " + CurrPin->Value + " IN")
          'Pin is written only
          ElseIf .WrittenTo Then
            CurrLine = LinkedListInsert(CurrLine, "DIR " + CurrPin->Value + " OUT")
          End If
        End If
        SkipPinDirSet:

      'Checking a port
      Else

        'If pin direction hasn't been set but is obvious, set it automatically
        If (Not .SetIn And Not .SetOut And .WholePort = 0) And (.ReadFrom Xor .WrittenTo) Then

          'Make sure no conflicting single pin direction set used
          SearchLine = PinDirections->Next
          Do While SearchLine <> 0
            If Left(SearchLine->Value, Len(CurrPin->Value)) = CurrPin->Value Then
              'Have found single setting
              'Don't set port if any bits are set
              'Port is actually pin
              CurrPortDir = SearchLine->MetaData
              'If whole port set at all, don't set automatically
              If CurrPortDir->SetIn Or CurrPortDir->SetOut Then GoTo SkipPortDirSet
              'If whole port dir clashes with single bit dir, don't set
              If Not (CurrPortDir->ReadFrom Or .ReadFrom) Xor (CurrPortDir->WrittenTo Or .WrittenTo) Then GoTo SkipPortDirSet

            End If
            SearchLine = SearchLine->Next
          Loop

          If .ReadFrom Then
            CurrLine = LinkedListInsert(CurrLine, "DIR " + CurrPin->Value + " IN")
          ElseIf .WrittenTo Then
            CurrLine = LinkedListInsert(CurrLine, "DIR " + CurrPin->Value + " OUT")
          End If
        End If

        SkipPortDirSet:
      End If

    End With

    CurrPin = CurrPin->Next
  Loop

  Return OutList
  End Function

Function GenerateBitSet(BitNameIn As String, NewStatus As String, Origin As String, CurrSub As SubType Pointer = 0, SetStatus As Integer = -1) As LinkedListElement Pointer
  'Set a given bit either on or off
  'When bit is addressed by variable, can call with SetStatus = 0 (false).
  'This will prevent any setting of the bit status for SysSetBit, which must be set before the code from this routine

  Dim As String InLine, Temp, BitName
  Dim As String VarName, VarType, VarBit, Status, VarNameOld, VarBitOld
  Dim As Integer FindShadow, BitAndValue
  Dim As VariableType Pointer VarFound

  Dim As LinkedListElement Pointer OutList, CurrLine

  OutList = LinkedListCreate
  CurrLine = OutList

  'Ensure subroutine is known (or assume main)
  If CurrSub = 0 Then
    IF INSTR(Origin, "D") <> 0 Then
      CurrSub = Subroutine(GetDestSub(Origin))
    Else
      CurrSub = Subroutine(GetSubID(Origin))
    End If
  End If

  'Get Bit var and name
  BitName = BitNameIn
  'If no var, might be dealing with an SFR bit
  If InStr(BitName, ".") = 0 Then
    BitName = GetWholeSFR(BitName)
  End If
  'Should now have a var!
  If InStr(BitName, ".") <> 0 Then
    VarName = Left(BitName, InStr(BitName, ".") - 1)
    VarBit = Mid(BitName, InStr(BitName, ".") + 1)
  Else
    Return OutList
  End If
  Status = NewStatus
  VarType = TypeOfVar(VarName, CurrSub)
  VarNameOld = VarName: VarBitOld = VarBit

  'Show error if used on invalid type
  Select Case VarType
    Case "BYTE", "WORD", "INTEGER", "LONG", "ULONGINT", "LONGINT", "SINGLE", "DOUBLE":
    'Do nothing
  Case Else
    'Show error
    Temp = Message("BadCommandType")
    Replace Temp, "%command%", "SET"
    Replace Temp, "%type%", LCase(VarType)
    LogError Temp, Origin
    Return OutList
  End Select

  If VarType = "BYTE" and Instr(VarBit, "," ) > 0 Then
    'Trap things like DIR PORTB.0,1,2 OUT
    Temp = Message("InvalidDirCommand")
    Replace Temp, "%status%", Status
    LogError Temp, Origin
    Return OutList
  End If

  'Status should be 0 or 1
  If Status <> "0" And Status <> "1" And SetStatus Then
    Temp = Message("BadSetStatus")
    Replace Temp, "%status%", Status
    LogError Temp, Origin
    Return OutList
  End If

  'Is bit number fixed?
  If Not IsConst(VarBit) And Not HasSFRBit(VarBit) Then
    'Bit number isn't a number or SFR, must be referenced by variable
    'Get maximum allowed bit number
    BitAndValue = GetTypeSize(VarType) * 8 - 1

    'Variable to operate on specified by FSR (or FSR0) on PIC or SysReadA on AVR
    'Bit to operate on comes from SysByteTempX
    'New state of bit is in bit 0 of SysByteTempB
    If ChipFamily = 12 Or ChipFamily = 14 Then
      CurrLine = LinkedListInsert(CurrLine, " movlw low " + VarName)
      CurrLine = LinkedListInsert(CurrLine, " movwf FSR")
      If HighFSR Then CurrLine = LinkedListInsert(CurrLine, " bankisel " + VarName)

    ElseIf ChipFamily = 15 Then
      CurrLine = LinkedListInsert(CurrLine, " movlw low " + VarName)
      CurrLine = LinkedListInsert(CurrLine, " movwf FSR0L")
      CurrLine = LinkedListInsert(CurrLine, " movlw high " + VarName)
      CurrLine = LinkedListInsert(CurrLine, " movwf FSR0H")

    ElseIf ChipFamily = 16 Then
      CurrLine = LinkedListInsert(CurrLine, " lfsr 0," + VarName)

    End If

    If ModePIC Then
      CurrLine = LinkedListInsert(CurrLine, " movlw " + Str(BitAndValue))
      
      CurrLine = LinkedListInsert(CurrLine, " andwf " + VarBit + ", W")
      CurrLine = LinkedListInsert(CurrLine, " movwf SysByteTempX")
      If SetStatus Then
        Temp = " bsf ": IF Status = "0" THEN Temp = " bcf "
        CurrLine = LinkedListInsert(CurrLine, Temp + "SysByteTempB, 0")
      End If
      CurrLine = LinkedListInsert(CurrLine, " call SysSetBit")

      RequestSub(CurrSub, "SysSetBit")
      If Not IsIOReg(VarName) Then AddVar VarName, "BYTE", 1, CurrSub, "REAL", Origin

    ElseIf ModeAVR Then
      CurrLine = LinkedListInsert(CurrLine, " ldi SysStringA, low(" + VarName + ")")
      CurrLine = LinkedListInsert(CurrLine, " ldi SysStringA_H, high(" + VarName + ")")

      CurrLine = LinkedListInsertList(CurrLine, CompileVarSet(VarBit, "SysReadA", Origin))
      CurrLine = LinkedListInsert(CurrLine, " andi SysReadA, " + Str(BitAndValue))

      If SetStatus Then
        Temp = " sbr ": IF Status = "0" THEN Temp = " cbr "
        CurrLine = LinkedListInsert(CurrLine, Temp + "SysByteTempB, 1")
      End If
      CurrLine = LinkedListInsert(CurrLine, " rcall SysSetBit")

      RequestSub(CurrSub, "SysSetBit")
      If Not IsIOReg(VarName) Then AddVar VarName, "BYTE", 1, CurrSub, "REAL", Origin
      AddVar "SysStringA", "WORD", 1, 0, "REAL", Origin, , -1 'Needs to be global

    End If

    'Request sequential bytes
    VarFound = HashMapGet(@(CurrSub->Variables), VarName)
    If VarFound <> 0 Then
      VarFound->NeedsSequentialLoc = -1
    End If

    Return OutList
  End If

  'If bit > 7, operate on high byte
  If Val(VarBit) > 7 And Val(VarBit) < 8 * GetTypeSize(VarType) Then
    VarName = GetByte(VarName, Val(VarBit) \ 8)
    VarBit = Str(Val(VarBit) Mod 8)
  End If

  'If bit still > 7, bit is wrong
  If Val(VarBit) > 7 Then
    Temp = Message("BadVarBit")
    Replace Temp, "%var%", VarNameOld
    Replace Temp, "%type%", LCase(VarType)
    Replace Temp, "%bit", VarBitOld

    If ChipAVRDX Then
      If Instr( Ucase(Temp), "_OUT") > 0 then Replace Temp, "_OUT", ""
      If Instr( Ucase(Temp), "_IN") > 0 then Replace Temp, "_IN", ""
    End If
    If Instr( Ucase(Temp), "%") > 0 then Replace Temp, "%", ""

    LogError Temp, Origin
    Return OutList
  End If

  If ModePIC Then
    'Redirect PORTx writes to LATx
    If UseChipOutLatches And Left(VarName, 4) = "PORT" And Len(VarName) = 5 Then
      If HasSFR(VarName) Then
        VarName = "LAT" + Right(VarName, 1)
      End If
    End If

    'Record individual setting of OPTION bits
    If ChipFamily = 12 Then
      If VarName = "OPTION_REG" And Not HasSFR(VarName) Then
        For FindShadow = 1 To PinDirShadows
          If PinDirShadow(FindShadow) = VarName Then GoTo OptionShadowFound
        Next
        PinDirShadows += 1
        PinDirShadow(PinDirShadows) = VarName
        OptionShadowFound:
      End If
    End If

    Temp = " bsf ": IF Status = "0" THEN Temp = " bcf "
    CurrLine = LinkedListInsert(CurrLine, Temp + VarName + "," + VarBit)
    If Not IsIOReg(VarName) Then AddVar VarName, "BYTE", 1, CurrSub, "REAL", Origin

  ElseIf ModeAVR Then
    IF VarName = "SREG" Then
      Temp = " se": IF Status = "0" THEN Temp = " cl"
      CurrLine = LinkedListInsert(CurrLine, Temp + LCase(Trim(VarBit)))
    ElseIf IsRegister(VarName) Then
      Temp = " sbr ": IF Status = "0" THEN Temp = " cbr "
      CurrLine = LinkedListInsert(CurrLine, Temp + " " + VarName + ",1<<" + VarBit)

    'Adapted to support chipfamily121
    'As chipfamily121 has USART registers that are NOT accessible using SBI and CBI instructions'
    'Luckily the USART registers start with U
    ElseIf IsLowIOReg(VarName) and not ( ChipFamily = 121 and Ucase(left(trim(VarName),1)) = "U" ) Then
      Temp = " sbi ": IF Status = "0" THEN Temp = " cbi "
      
      If ChipAVRDX and IsIORegDX(VarName) Then
        'AVRDX approach to set a bit
        'Map legacy AVR register to DX register using the Alias Addressing
        If (( compilerdebug and cAVRDXDEBUG ) = cAVRDXDEBUG )  Then
          CurrLine = LinkedListInsert(CurrLine, ";IOAVRDX #2")
        End if

        Temp = " sbr ": IF Status = "0" THEN Temp = " cbr "
        AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
        'Get the current value in z, copy z into SysValueCopy, then write back from Z
        CurrLine = LinkedListInsert(CurrLine, " lds SysValueCopy, " +  GetSysVarAliasName ( VarName ))
        CurrLine = LinkedListInsert(CurrLine, Temp + "SysValueCopy" + ",1<<" + VarBit)
        CurrLine = LinkedListInsert(CurrLine, " sts " + GetSysVarAliasName ( VarName ) + ", SysValueCopy")
      Else
        'Original MegaAVR approach to set a bit
        CurrLine = LinkedListInsert(CurrLine, Temp + VarName + "," + VarBit)
      End If
    ElseIf IsIOReg(VarName) Then
      Temp = " sbr ": IF Status = "0" THEN Temp = " cbr "
      CurrLine = LinkedListInsert(CurrLine, " in SysValueCopy," + VarName)
      CurrLine = LinkedListInsert(CurrLine, Temp + "SysValueCopy,1<<" + VarBit)
      CurrLine = LinkedListInsert(CurrLine, " out " + VarName + ",SysValueCopy")
      AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1

    Else
      Temp = " sbr ": IF Status = "0" THEN Temp = " cbr "
      CurrLine = LinkedListInsert(CurrLine, " lds SysValueCopy," + VarName)
      CurrLine = LinkedListInsert(CurrLine, Temp + "SysValueCopy,1<<" + VarBit)
      CurrLine = LinkedListInsert(CurrLine, " sts " + VarName + ",SysValueCopy")
      AddVar "SysValueCopy", "BYTE", 1, 0, "REAL", "", , -1
      AddVar VarName, "BYTE", 1, CurrSub, "REAL", Origin
    End If

  ElseIf ModeZ8 Then
    If Status = "0" Then
      CurrLine = LinkedListInsert(CurrLine, " orx " + VarName + ", #(1 <<" + Trim(VarBit) + ")" )
    Else
      CurrLine = LinkedListInsert(CurrLine, " andx " + VarName + ", #!(1 <<" + Trim(VarBit) + ")")
    End If

  End If

  Return OutList
  End Function

Function GenerateExactDelay(ByVal Cycles As Integer) As LinkedListElement Pointer

  Dim As Integer DS ', AddDelayTemp, AddDelayTemp2
  Dim As LinkedListElement Pointer OutList, CurrPos
  OutList = LinkedListCreate
  CurrPos = OutList

  'AddDelayTemp = 0
  Do
    If ModePIC Then
      If Cycles >= 772 Then
        DS = (Cycles - 1) \ 771
        If DS > 255 Then DS = 255
        Cycles -= 1 + DS * 771
        CurrPos = LinkedListInsert(CurrPos, "DELAYTEMP2 = " + Str(DS))
        CurrPos = LinkedListInsert(CurrPos, "DelayUSO" + Str(USDC))
        GetMetaData(CurrPos)->IsLabel = -1
        CurrPos = LinkedListInsert(CurrPos, "DELAYTEMP = 0")
        CurrPos = LinkedListInsert(CurrPos, "DelayUS" + Str(USDC))
        GetMetaData(CurrPos)->IsLabel = -1
        CurrPos = LinkedListInsert(CurrPos, " decfsz DELAYTEMP,F")
        CurrPos = LinkedListInsert(CurrPos, " goto DelayUS" + Str(USDC))
        CurrPos = LinkedListInsert(CurrPos, " decfsz DELAYTEMP2,F")
        CurrPos = LinkedListInsert(CurrPos, " goto DelayUSO" + Str(USDC))
        USDC += 1
      ElseIf Cycles >= 5 Then
        DS = Cycles \ 3
        IF DS > 255 THEN DS = 255
        Cycles -= (DS * 3 + 1) 'lose 1 cycle at loop end, but have 2 extra at start
        CurrPos = LinkedListInsert(CurrPos, "DELAYTEMP = " + Str(DS)) '2 cycles, movlw/movwf
        CurrPos = LinkedListInsert(CurrPos, "DelayUS" + Str(USDC))
        GetMetaData(CurrPos)->IsLabel = -1
        CurrPos = LinkedListInsert(CurrPos, " decfsz DELAYTEMP,F")
        CurrPos = LinkedListInsert(CurrPos, " goto DelayUS" + Str(USDC))
        USDC += 1
      ElseIf Cycles >= 2 AND ChipFamily <> 16 THEN
        CurrPos = LinkedListInsert(CurrPos, " goto $+1")
        Cycles -= 2
      ElseIf Cycles >= 1 THEN
        CurrPos = LinkedListInsert(CurrPos, " nop")
        Cycles -= 1
      END If

    ElseIf ModeAVR Then
      If Cycles >= 771 Then
        'Each 1 in DS wastes 3 + 3 * 256 = 771 us
        DS = Cycles \ 771
        If DS > 255 Then DS = 255
        Cycles -= (DS * 771)
        CurrPos = LinkedListInsert(CurrPos, "DELAYTEMP2 = " + Str(DS))
        CurrPos = LinkedListInsert(CurrPos, "DelayUSO" + Str(USDC) + LabelEnd)
        CurrPos = LinkedListInsert(CurrPos, "DELAYTEMP = 0")
        CurrPos = LinkedListInsert(CurrPos, "DelayUS" + Str(USDC) + LabelEnd)
        CurrPos = LinkedListInsert(CurrPos, " dec DELAYTEMP")
        CurrPos = LinkedListInsert(CurrPos, " brne DelayUS" + Str(USDC))
        CurrPos = LinkedListInsert(CurrPos, " dec DELAYTEMP2")
        CurrPos = LinkedListInsert(CurrPos, " brne DelayUSO" + Str(USDC))
        USDC += 1

      ElseIf Cycles >= 6 Then
        'This delay can do multiples of 3 cycles
        'But for 3 cycles, it's more efficient to have the rjmp then the nop
        DS = Cycles \ 3
        IF DS > 255 THEN DS = 255
        Cycles -= (DS * 3) 'lose 1 cycle at loop end, but have 1 extra at start
        CurrPos = LinkedListInsert(CurrPos, "DELAYTEMP = " + Str(DS))
        CurrPos = LinkedListInsert(CurrPos, "DelayUS" + Str(USDC) + LabelEnd)
        CurrPos = LinkedListInsert(CurrPos, " dec DELAYTEMP")
        CurrPos = LinkedListInsert(CurrPos, " brne DelayUS" + Str(USDC))
        USDC += 1
      ElseIf Cycles >= 2 Then
        CurrPos = LinkedListInsert(CurrPos, " rjmp PC + 1") ' Was rjmp 0
        Cycles -= 2
      ElseIf Cycles >= 1 Then
        CurrPos = LinkedListInsert(CurrPos, " nop")
        Cycles -= 1
      End If

    Else

    End If
  Loop While Cycles > 0

  Return OutList
  End Function

Function GenerateMultiSet(SourceData As String, DestVar As String, Origin As String, CurrSub As SubType Pointer, CanSkip As Integer) As LinkedListElement Pointer
  'Generate code to set multiple bits in destvar to appropriate values from sourcedata.
  'CanSkip is set if it is OK to skip non-existent bits without an error

  Dim As LinkedListElement Pointer OutList, CurrLine, OutBits, CurrOutBit
  Dim As Integer SourceValue, CurrBitNo
  Dim As String CurrBitVal, Temp

  'Prepare
  OutList = LinkedListCreate
  CurrLine = OutList

  'Source data must be a constant
  If Not IsConst(SourceData) Then
    'Error
    LogError Message("MultiSetLiteral"), Origin
    Return OutList
  Else
    SourceValue = MakeDec(SourceData)
  End If

  'Get individual output bits
  OutBits = GetElements(DestVar, ",")
  CurrBitNo = LinkedListSize(OutBits) - 1
  CurrOutBit = OutBits->Next
  Do While CurrOutBit <> 0
    'Set bit
    CurrBitVal = "0"
    If (SourceValue And 2 ^ CurrBitNo) <> 0 Then CurrBitVal = "1"
    If InStr(CurrOutBit->Value, ".") = 0 And Not CanSkip Then
      'Error, bit is invalid
      Temp = Message("InvalidBit")
      Replace Temp, "%bit%", CurrOutBit->Value
      LogError Temp, Origin
    Else
      CurrLine = LinkedListInsertList(CurrLine, GenerateBitSet(CurrOutBit->Value, CurrBitVal, Origin, CurrSub))
    End If

    CurrBitNo -= 1
    CurrOutBit = CurrOutBit->Next
  Loop

  Return OutList
  End Function

Function GenerateVectorCode As LinkedListElement Pointer

  Dim As Integer VectsAdded, CurrentVect, IntLoc, PD

  'Prepare output list
  Dim As LinkedListElement Pointer OutList, CurrLine
  Dim As SubType Pointer IntSub
  OutList = LinkedListCreate
  CurrLine = OutList

  CurrLine = LinkedListInsert(CurrLine, ";Vectors")
  'Generate PIC vectors
  If ModePIC Then
    '12 and 14 bit instruction width cores
    If ChipFamily = 12 Or ChipFamily = 14 Or ChipFamily = 15 Then
      If HasSFR("INTCON") Then
        If UserCodeOnlyEnabled = 0 then
            CurrLine = LinkedListInsert(CurrLine, " ORG " + Str(Bootloader))
            CurrLine = LinkedListInsert(CurrLine, " pagesel BASPROGRAMSTART")
            CurrLine = LinkedListInsert(CurrLine, " goto BASPROGRAMSTART")
            CurrLine = LinkedListInsert(CurrLine, " ORG " + Str(Bootloader + 4))
            IntLoc = LocationOfSub("INTERRUPT", "")   'was capitalised
        Else 
            CurrLine = LinkedListInsert(CurrLine, " ORG " + Str(Bootloader))
            CurrLine = LinkedListInsert(CurrLine, " pagesel " + UserDefineStartLabel)
            CurrLine = LinkedListInsert(CurrLine, " goto " + UserDefineStartLabel)
          End If

        If IntLoc = 0 Then
          If UserCodeOnlyEnabled = 0 then
              CurrLine = LinkedListInsert(CurrLine, " retfie")
          End If
        Else
          'Can't do this, must compile INTERRUPT inline
          'CurrLine = LinkedListInsert(CurrLine, " goto INTERRUPT")
          CurrLine = LinkedListInsert(CurrLine, "INTERRUPT")   'was capitalised
          GetMetaData(Currline)->IsLabel = -1
          'Inline later
        End If
        CurrLine = LinkedListInsert(CurrLine, "")
        CurrLine = LinkedListInsert(CurrLine, Star80)
        CurrLine = LinkedListInsert(CurrLine, "")
      Else
        'Do nothing, page 0 will start immediately
      End If

    '16 bit instruction width cores
    ElseIf ChipFamily = 16 Then
      If UserCodeOnlyEnabled = 0 then
          CurrLine = LinkedListInsert(CurrLine, " ORG " + Str(Bootloader))
          CurrLine = LinkedListInsert(CurrLine, " goto BASPROGRAMSTART")
          CurrLine = LinkedListInsert(CurrLine, " ORG " + Str(Bootloader + 8))
          If LocationOfSub("INTERRUPT", "") = 0 Then
            CurrLine = LinkedListInsert(CurrLine, " retfie")
          Else
            CurrLine = LinkedListInsert(CurrLine, " goto INTERRUPT")
          End If
          CurrLine = LinkedListInsert(CurrLine, "")
          CurrLine = LinkedListInsert(CurrLine, Star80)
          CurrLine = LinkedListInsert(CurrLine, "")
      else
          CurrLine = LinkedListInsert(CurrLine, " ORG " + Str(Bootloader))
          CurrLine = LinkedListInsert(CurrLine, " goto "+ UserDefineStartLabel)
          'No Interrupt handler is supported
          '          CurrLine = LinkedListInsert(CurrLine, " ORG " + Str(Bootloader + 8))
          '          If LocationOfSub("INTERRUPT", "") = 0 Then     'was capitalised
          '            CurrLine = LinkedListInsert(CurrLine, " retfie")
          '          Else
          '            CurrLine = LinkedListInsert(CurrLine, " goto INTERRUPT")
          '          End If
          CurrLine = LinkedListInsert(CurrLine, "")
          CurrLine = LinkedListInsert(CurrLine, Star80)
          CurrLine = LinkedListInsert(CurrLine, "")
      End if
    End If

  'AVR vectors
  ElseIf ModeAVR Then
    CurrLine = LinkedListInsert(CurrLine, ";Interrupt vectors")
    CurrLine = LinkedListInsert(CurrLine, Chr(9) + ".ORG " + Str(Bootloader))
    If ChipFamily <> 122 then
      CurrLine = LinkedListInsert(CurrLine, " rjmp BASPROGRAMSTART ;Reset")
    Else
      If VBS = 1 then
        Print Message( "Family122OptimisationMessage" )
      End If
      CurrLine = LinkedListInsert(CurrLine, " jmp BASPROGRAMSTART ;Reset  " + DISABLEOPTIMISATION )

    End If

    'Add ON ... GOSub code
    VectsAdded = 0
    CurrentVect = 0
    ISRC = 0
    Do While VectsAdded < IntCount and  CurrentVect < 1024  'add < 1025 test reverting change 1664
      CurrentVect += 1
      ' Print CurrentVect, IntCount, VectsAdded
      If CurrentVect = 1024 then 
          LogWarning "Interrupt vectors are overloading compiler - check [Interrupt] section of DAT file for duplicates"
      End If
      IntLoc = 0
      For PD = 1 to IntCount
        If Interrupts(PD).VectorLoc = CurrentVect Then IntLoc = PD: Exit For
      Next

      If IntLoc <> 0 Then
        With Interrupts(IntLoc)
          VectsAdded += 1
              CurrLine = LinkedListInsert(CurrLine, chr(9) + ".ORG " + Str(.VectorLoc + Bootloader))
              If .Handler = "" Then
                IF UserInt THEN
                  If ChipFamily <> 122 then
                      CurrLine = LinkedListInsert(CurrLine, " rjmp INTERRUPT ;" + UCase(.Vector) )
                  Else
                      CurrLine = LinkedListInsert(CurrLine, " jmp INTERRUPT ;" + UCase(.Vector) + left("                    ", 20-len(UCase(.Vector))) + DISABLEOPTIMISATION)
                  End If
                Else
                  If ChipFamily <> 122 then
                    CurrLine = LinkedListInsert(CurrLine, " reti ;" + UCase(.Vector))
                  Else
                    CurrLine = LinkedListInsert(CurrLine, " jmp 0  ;" + UCase(.Vector)  + left("                    ", 20-len(UCase(.Vector))) + DISABLEOPTIMISATION)
                  End If
                End If
              Else
                If ChipFamily <> 122 then
                  CurrLine = LinkedListInsert(CurrLine, " rjmp Int" + UCase(.Vector) + " ;" + UCase(.Vector))
                Else
                  CurrLine = LinkedListInsert(CurrLine, " jmp Int" + UCase(.Vector) + " ;" + UCase(.Vector)+ left("                    ", 20-len(UCase(.Vector))) + DISABLEOPTIMISATION)
                End if
              End If
        End With
      End If

    Loop

    CurrLine = LinkedListInsert(CurrLine, "")
    CurrLine = LinkedListInsert(CurrLine, Star80)
    CurrLine = LinkedListInsert(CurrLine, "")

  'Z8 vectors
  ElseIf ModeZ8 Then
    'Add ON ... GOSub code
    For IntLoc = 1 To IntCount
      With Interrupts(IntLoc)
        If UCase(.Vector) = "RESET" Then
          CurrLine = LinkedListInsert(CurrLine, " VECTOR " + .Vector + " = BASPROGRAMSTART")
        Else
          If .Handler = "" Then
            IF UserInt THEN
              CurrLine = LinkedListInsert(CurrLine, " VECTOR " + .Vector + " = INTERRUPT")
            End If
          Else
            CurrLine = LinkedListInsert(CurrLine, " VECTOR " + .Vector + " = Int" + UCase(.Vector))
          End If
        End If
      End With
    Next

  End If

  Return OutList
  End Function

Function GetCalcType(VT1 As String, Act As String, VT2 As String, AnswerType As String) As String
  'Decide which type a calculation returns

  'Comparision operations return byte
  If Act = "=" Then Return "BYTE"
  If Act = "~" Then Return "BYTE"
  If Act = "<" Then Return "BYTE"
  If Act = ">" Then Return "BYTE"
  If Act = "{" Then Return "BYTE"
  If Act = "}" Then Return "BYTE"

  'Negate returns at least an integer
  If Act = "-" And VT1 = "" And CastOrder(VT2) < CastOrder("INTEGER") Then Return "INTEGER"

  'Multiply, divide and remainder operations return highest of two operands
  'Also return highest of two input types if no answer type
  If Act = "*" Or Act = "/" Or Act = "%" Or AnswerType = "" Then
    If CastOrder(VT1) > CastOrder(VT2) Then Return VT1
    Return VT2
  End If

  'Other operations return answer type if known
  Return AnswerType
  End Function

Function GetCalcVar (VarTypeIn As String) As String
  'Get a calc var, and return its name
  Dim As String VarType, Temp
  Dim As Integer OutVar, HighReg, SV

  'Status can be A (available), U (used) or empty

  OutVar = -1
  HighReg = 0
  VarType = UCase(Trim(VarTypeIn))
  If Mid(VarType, 2, 1) = ":" Then
    Temp = Left(VarType, 1)
    VarType = Mid(VarType, 3)
    IF ModeAVR And Temp = "H" Then HighReg = -1
  End If

  'Find old, unused var
  'Aim to achieve highest of following:
  '= Type, = area
  '> Type, = area
  '* Type, = area
  '= Type, > area
  '>= Type, > area
  '* Type, > area
  'If still none, make new

  'Correct type, correct area
  For SV = 1 to TCVC
    With CalcVars(SV)
      If .MaxType = VarType And .Status = "A" And HighReg = .High Then
        OutVar = SV
        Exit For
      End If
    End With
  Next

  'Any type that is bigger, correct area
  If OutVar = -1 Then
    For SV = 1 to TCVC
      With CalcVars(SV)
        If CastOrder(.MaxType) > CastOrder(VarType) And .Status = "A" And HighReg = .High Then
          OutVar = SV
          Exit For
        End If
      End With
    Next
  End If

  'Any type, correct area
  If OutVar = -1 Then
    For SV = 1 to TCVC
      With CalcVars(SV)
        If .Status = "A" And HighReg = .High Then
          OutVar = SV
          .MaxType = VarType 'Increase type
          Exit For
        End If
      End With
    Next
  End If

  'Correct type, higher area
  If Not HighReg Then
    If OutVar = -1 Then
      For SV = 1 to TCVC
        With CalcVars(SV)
          If .MaxType = VarType And .Status = "A" Then
            OutVar = SV
            Exit For
          End If
        End With
      Next
    End If

    'Any type that is bigger, higher area
    If OutVar = -1 Then
      For SV = 1 to TCVC
        With CalcVars(SV)
          If CastOrder(.MaxType) > CastOrder(VarType) And .Status = "A" Then
            OutVar = SV
            Exit For
          End If
        End With
      Next
    End If

    'Any type, higher area
    If OutVar = -1 Then
      For SV = 1 to TCVC
        With CalcVars(SV)
          If .Status = "A" Then
            OutVar = SV
            .MaxType = VarType 'Increase type
            Exit For
          End If
        End WIth
      Next
    End If
  End If

  'If no unused vars, make new one
  If OutVar = -1 Then
    TCVC += 1
    OutVar = TCVC
    With CalcVars(OutVar)
      .MaxType = VarType
      .High = 0
    End With
  End If

  'Mark var as used, set type and return name
  With CalcVars(OutVar)
    .Status = "U" 'Mark used
    .CurrentType = VarType 'Set type
    .CurrentValue = "" 'Clear value
    If HighReg Then
      .High = -1
    End If
  End With
  Return "SysTemp" + Str(OutVar)
  
  End Function

Function GetCalledSubs(CurrSub As SubType Pointer, ExistingList As LinkedListElement Pointer, FindCallers As Integer) As LinkedListElement Pointer
  'If FindCallers is 0, gets a list of all subs called from CurrSub, and any subs called from those.
  'If FindCallers is -1, work in reverse - find all subs that call CurrSub, and any subs that call those.

  Dim As LinkedListElement Pointer OutList, CurrPos, SearchList, CurrCall
  Dim As Integer CallFound

  If ExistingList = 0 Then
    OutList = LinkedListCreate
  Else
    OutList = ExistingList
  End If
  CurrPos = OutList
  Do While CurrPos->Next <> 0
    CurrPos = CurrPos->Next
  Loop

  With *CurrSub
    If FindCallers Then
      CurrCall = .CallerList->Next
    Else
      CurrCall = .CallList->Next
    End If
    Do While CurrCall <> 0

      'Check to see if call is already in output list
      SearchList = OutList->Next
      CallFound = 0
      Do While SearchList <> 0
        If SearchList->MetaData = CurrCall->MetaData Then
          CallFound = -1
          Exit Do
        End If
        SearchList = SearchList->Next
      Loop
      If CallFound = 0 Then
        CurrPos = LinkedListInsert(CurrPos, CurrCall->MetaData)
        GetCalledSubs(CurrCall->MetaData, OutList, FindCallers)
        Do While CurrPos->Next <> 0
          CurrPos = CurrPos->Next
        Loop
      End If

      CurrCall = CurrCall->Next
    Loop
  End With

  Return OutList
  End Function

Function GetDestSub(Origin As String) As Integer
  Dim As String Temp

  If Origin = "ALL" OR Origin = "" Then Return 0
  If InStr(Origin, "D") = 0 Then Return GetSubID(Origin)
  Return VAL(Trim(Mid(Origin, INSTR(Origin, "D") + 1)))
  
  End Function

Sub GetEqConfig
  'Generate a list of all config names/settings that are equivalent

  Dim As LinkedListElement Pointer CurrLoc, EqSettingsLoc

  If ModePIC Then
    'EqConfigSettings is a linked list of linked lists
    'Each Sub list in the list contains all equivalent settings
    EqConfigSettings = LinkedListCreate
    EqSettingsLoc = EqConfigSettings

    'MCLR/MCLRE
    CurrLoc = LinkedListCreate
    EqSettingsLoc = LinkedListInsert(EqSettingsLoc, CurrLoc)
    CurrLoc = LinkedListInsert(CurrLoc, "MCLR")
    CurrLoc = LinkedListInsert(CurrLoc, "MCLRE")
    CurrLoc = LinkedListInsert(CurrLoc, "EXTMCLR")

    'OSC/FOSC/EXTRC/INTRC/INTIO
    CurrLoc = LinkedListCreate
    EqSettingsLoc = LinkedListInsert(EqSettingsLoc, CurrLoc)
    CurrLoc = LinkedListInsert(CurrLoc, "OSC")
    CurrLoc = LinkedListInsert(CurrLoc, "FOSC")
    CurrLoc = LinkedListInsert(CurrLoc, "EXTRC")
    CurrLoc = LinkedListInsert(CurrLoc, "INTRC")
    CurrLoc = LinkedListInsert(CurrLoc, "INTIO")
    CurrLoc = LinkedListInsert(CurrLoc, "RSTOSC")

    'BODEN/BOREN/BOD/BOR
    CurrLoc = LinkedListCreate
    EqSettingsLoc = LinkedListInsert(EqSettingsLoc, CurrLoc)
    CurrLoc = LinkedListInsert(CurrLoc, "BODEN")
    CurrLoc = LinkedListInsert(CurrLoc, "BOREN")
    CurrLoc = LinkedListInsert(CurrLoc, "BOD")
    CurrLoc = LinkedListInsert(CurrLoc, "BOR")

    'WDT/WDTEN
    CurrLoc = LinkedListCreate
    EqSettingsLoc = LinkedListInsert(EqSettingsLoc, CurrLoc)
    CurrLoc = LinkedListInsert(CurrLoc, "WDT")
    CurrLoc = LinkedListInsert(CurrLoc, "WDTE")
    CurrLoc = LinkedListInsert(CurrLoc, "WDTEN")

    'PWRT/PWRTE
    CurrLoc = LinkedListCreate
    EqSettingsLoc = LinkedListInsert(EqSettingsLoc, CurrLoc)
    CurrLoc = LinkedListInsert(CurrLoc, "PWRT")
    CurrLoc = LinkedListInsert(CurrLoc, "PWRTE")

    'CCP1/CCPMX
    CurrLoc = LinkedListCreate
    EqSettingsLoc = LinkedListInsert(EqSettingsLoc, CurrLoc)
    CurrLoc = LinkedListInsert(CurrLoc, "CCP1")
    CurrLoc = LinkedListInsert(CurrLoc, "CCPMX")

  End If
  
  End Sub

Function GetLabelList(CompSub As SubType Pointer) As LinkedListElement Pointer

  'Search a subroutine, return a list of all labels in it

  Dim As LinkedListElement Pointer LabelList, LabelListPos, CurrLine

  LabelList = LinkedListCreate
  LabelListPos = LabelList

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    If IsASM(CurrLine->Value, 1) = 0 Then
      If LabelEnd <> "" Then
        If Right(CurrLine->Value, 1) = LabelEnd Then
          LabelListPos = LinkedListInsert(LabelListPos, UCase(Left(CurrLine->Value, Len(CurrLine->Value) - 1)))
          LabelListPos->MetaData = CurrLine
        End If
      Else
        If CurrLine->Value <> "" And Left(CurrLine->Value, 1) <> ";" And Left(CurrLine->Value, 1) <> " " And Left(CurrLine->Value, 9) <> "PRESERVE " Then
          LabelListPos = LinkedListInsert(LabelListPos, UCase(CurrLine->Value))
          LabelListPos->MetaData = CurrLine
        End If
      End If
    End If
    CurrLine = CurrLine->Next
  Loop

  Return LabelList
  End Function

Function GetLinearLoc(Location As Integer) As Integer
  'On 16F1 chips, convert a non-linear address to a linear address
  'Mapping: 0x20 to 0x6F > 0x2000 to 0x204F
  '         0xA0 to 0xEF > 0x2050 to 0x209F
  '         0x120 to 0x16F > 0x20A0 to 0x20EF

  If ChipFamily <> 15 Then Return Location
  Dim As Integer Bank, BankLoc
  'Get bank and location in bank
  Bank = Location \ 128
  BankLoc = Location And 127
  'If location isn't available in linear memory, return unchanged
  If BankLoc < 32 Then Return Location
  If BankLoc > 111 Then Return Location

  Return &H2000 + Bank * 80 + (BankLoc - 32)  
  End Function

Function GetNonLinearLoc(Location As Integer) As Integer
  'On 16F1 chips, convert a linear address to a non-linear address
  'Mapping: 0x20 to 0x6F < 0x2000 to 0x204F
  '         0xA0 to 0xEF < 0x2050 to 0x209F
  '         0x120 to 0x16F < 0x20A0 to 0x20EF

  'If not 16F1, or not linear location, return unchanged
  If ChipFamily <> 15 Then Return Location
  If Location >= &H8000 Then Return Location
  If Location < &H2000 Then Return Location

  Dim As Integer TempLoc, Bank, BankLoc

  'Get bank and location in bank
  TempLoc = Location - &H2000
  Bank = TempLoc \ 80
  BankLoc = (TempLoc - 80 * Bank) + 32

  Return Bank * 128 + BankLoc
  End Function

Function GetMetaData(CurrLine As LinkedListElement Pointer) As ProgLineMeta Pointer
  'Get the ProgLineMeta object for the current line
  'If none exists, create one

  'If CurrLine is null pointer, return null pointer
  If CurrLine = 0 Then Return 0

  'Check for metadata, create if needed
  If CurrLine->MetaData = 0 Then
    CurrLine->MetaData = NewProgLineMeta
  End If

  Return CurrLine->MetaData
  
  End Function

Function GetPinDirection(PinNameIn As String) As PinDirType Pointer
  'Get the PinDirections list element metadata for the pin
  'Will add the pin to the list if no data found for it

  Dim As String PinName, CheckPin, PinDirVar, PinBit
  Dim As LinkedListElement Pointer CurrItem, PinListItem
  Dim As PinDirType Pointer PinDirItem
  Dim As SysVarType Pointer PinBitVar

  PinName = UCase(Trim(PinNameIn))

  'Check that pin is a valid I/O pin
  'Return 0 if it isn't
  'Should be in form {PORT | GPIO | LAT | PIN}[A|B|C|D|E]
  'Can also accept an entire port
  If InStr(PinName, ".") = 0 Then
    CheckPin = PinName
  Else
    CheckPin = Left(PinName, InStr(PinName, ".") - 1)
  End If

  'If CheckPin is not PORTx Or PORTxx,
  If Left(CheckPin, 4) <> "PORT" Or (Len(CheckPin) <> 5 And Len(CheckPin) <> 6) Then
    'and not GPIO,
    If CheckPin <> "GPIO" Then
      'and not LATx,
      If Left(CheckPin, 3) <> "LAT" Or Len(CheckPin) <> 4 Then
        'and not PINx,
        If Left(CheckPin, 3) <> "PIN" Or Len(CheckPin) <> 4 Then
          'then CheckPin is not a valid pin
          Return 0
        End If
      End If
    End If
  End If
  'Not IO, not a pin
  If GetSysVar(CheckPin) = 0 Then Return 0

  'Check that direction of pin can be set
  'Get the variable that sets port direction
  PinDirVar = CheckPin
  Replace PinDirVar, "PORT", ""
  Replace PinDirVar, "GP", ""
  Replace PinDirVar, "LAT", ""
  Replace PinDirVar, "PIN", ""
  If ModePIC Then
    PinDirVar = "TRIS" + PinDirVar
  ElseIf ModeAVR Then
    PinDirVar = "DDR" + PinDirVar
  End If
  'TRISIO will not be IO on 12 bit PIC, but will be used as direction buffer
  If ChipFamily <> 12 And GetSysVar(PinDirVar) = 0 Then Return 0

  'If pin name starts with LAT or PIN, replace with PORT
  If Left(PinName, 3) = "LAT" Then PinName = "PORT" + Mid(PinName, 4)
  If Left(PinName, 3) = "PIN" Then PinName = "PORT" + Mid(PinName, 4)

  If InStr(PinName, ".") <> 0 Then
    PinBit = Mid(PinName, InStr(PinName, ".") + 1)
    If Not IsConst(PinBit) Then
      PinBitVar = HashMapGet(SysVarBits, PinBit)
      If PinBitVar <> 0 Then
        PinName = CheckPin + "." + Str(PinBitVar->Location)
      Else
        'Bit is unknown, cannot set
        Return 0
      End If
    End If
  End If

  'Check for pin in existing pin direction list
  PinListItem = 0
  CurrItem = PinDirections->Next
  Do While CurrItem <> 0
    If CurrItem->Value = PinName Then
      PinListItem = CurrItem
    End If
    CurrItem = CurrItem->Next
  Loop

  'If pin isn't in list, add it
  If PinListItem = 0 Then
    PinListItem = LinkedListInsert(PinDirections, PinName)
  End If

  'Get pin direction data
  PinDirItem = PinListItem->MetaData
  'If no direction data exists, create it
  If PinDirItem = 0 Then
    PinDirItem = Callocate(SizeOf(PinDirType))
    PinListItem->MetaData = PinDirItem
  End If

  'Return direction data
  Return PinDirItem
  
  End Function

Function GetRealIOName(InName As String) As String
  'Used on AVR to improve compatibility with existing GCBASIC programs
  'Typically called when generating pin reading code
  'Changes PORTx to PINx, and LATx to PORTx, making AVR and PIC18 compatible

  'AVR only
  If Not ModeAVR Then Return InName

  Dim As String OutName
  'Do replacements
  If Left(UCase(InName), 4) = "PORT" And Len(InName) = 5 Then
    OutName = "PIN" + Mid(InName, 5)
  ElseIf Left(UCase(InName), 3) = "LAT" And Len(InName) = 4 Then
    OutName = "PORT" + Mid(InName, 4)
  Else
    OutName = InName
  End If

  Return OutName
  End Function

Function GetRegisterLoc(RegName As String) As Integer

  'Returns the location for a system variable
  'Returns -1 if no location found

  Dim RegNameTidy As String
  Dim As Integer DestLoc, CurrBank
  DestLoc = -1

  If ModePIC Then
    'Get the location for the register
    RegNameTidy = LCase(Trim(RegName))
    Select Case RegNameTidy

      Case "syscalctempx", "sysbytetempx", "syswordtempx", "sysintegertempx", "syslongtempx", "delaytemp": DestLoc = 0
      Case "syscalctempx_h", "syswordtempx_h", "sysintegertempx_h", "syslongtempx_h", "delaytemp2": DestLoc = 1
      Case "syscalctempx_u", "syslongtempx_u", "sysdivmultx", "syswaittempms", "sysstringb": DestLoc = 2
      Case "syscalctempx_e", "syslongtempx_e", "sysdivmultx_h", "syswaittempms_h", "sysstringb_h": DestLoc = 3
      Case "sysdivloop", "syswaittemp10ms", "syswaittemps": DestLoc = 4
      Case "syscalctempa", "sysbytetempa", "syswordtempa", "sysintegertempa", "syslongtempa": DestLoc = 5
      Case "syswaittempus", "syswaittemp10us", "syswaittempm": DestLoc = 5
      Case "syscalctempa_h", "syswordtempa_h", "sysintegertempa_h", "syslongtempa_h": DestLoc = 6
      Case "sysstringlength", "syswaittempus_h", "syswaittemph": DestLoc = 6
      Case "syscalctempa_u", "syslongtempa_u", "sysdivmulta", "sysstringa": DestLoc = 7
      Case "syscalctempa_e", "syslongtempa_e", "sysdivmulta_h", "sysstringa_h": DestLoc = 8
      Case "syscalctempb", "sysbytetempb", "syswordtempb", "sysintegertempb", "syslongtempb": DestLoc = 9
      Case "syscalctempb_h", "syswordtempb_h", "sysintegertempb_h", "syslongtempb_h": DestLoc = 10
      Case "syscalctempb_u", "syslongtempb_u", "sysdivmultb", "sysreada": DestLoc = 11
      Case "syscalctempb_e", "syslongtempb_e", "sysdivmultb_h", "sysreada_h": DestLoc = 12
      Case "syssignbyte": DestLoc = 13
      Case "sysw": DestLoc = 14
      Case "sysstatus": DestLoc = 15
    End Select

    'Check that DestLoc actually exists
    If DestLoc <> -1 Then
      For CurrBank = 1 To NoBankLocs
        With NoBankLoc(CurrBank)
          'If the location is in a shared bank, return location
          If .StartLoc + DestLoc <= .EndLoc Then
            Return DestLoc
          End If
        End With
      Next
      DestLoc = -1
    End If

  ElseIf ModeAVR Then
    'Get the location for the register
    RegNameTidy = LCase(Trim(RegName))
    Select Case RegNameTidy
      Case "syscalctempx", "sysbytetempx", "syswordtempx", "sysintegertempx", "syslongtempx": 'DestLoc = 0
          Select Case ChipGPR
            Case 16
              DestLoc = 16
            Case Else
              DestLoc = 0
          End Select

      Case "syscalctempx_h", "syswordtempx_h", "sysintegertempx_h", "syslongtempx_h":'DestLoc = 1
          Select Case ChipGPR
            Case 16
              DestLoc = 17
            Case Else
              DestLoc = 1
          End Select
      Case "syscalctempx_u", "syslongtempx_u", "sysdivmultx": 'DestLoc = 2
          Select Case ChipGPR
            Case 16
              DestLoc = 18
            Case Else
              DestLoc = 2
          End Select

      Case "syscalctempx_e", "syslongtempx_e", "sysdivmultx_h": 'DestLoc = 3
          Select Case ChipGPR
            Case 16
              DestLoc = 19
            Case Else
              DestLoc = 3
          End Select

      Case "syssignbyte": 'DestLoc = 4
          Select Case ChipGPR
            Case 16
              'usE ram
              'DestLoc = 4
            Case Else
              DestLoc = 4
          End Select

      Case "sysdivloop", "sysbittest": 'DestLoc = 5
          Select Case ChipGPR
            Case 16
              DestLoc = 20
            Case Else
              DestLoc = 5
          End Select


      Case "sysvaluecopy": DestLoc = 21
      Case "syscalctempa", "sysbytetempa", "syswordtempa", "sysintegertempa", "syslongtempa": DestLoc = 22
      Case "syscalctempa_h", "syswordtempa_h", "sysintegertempa_h", "syslongtempa_h": DestLoc = 23
      Case "syscalctempa_u", "syslongtempa_u", "sysdivmulta": DestLoc = 24
      Case "delaytemp", "syscalctempa_e", "syslongtempa_e", "sysdivmulta_h", "sysstringlength": DestLoc = 25
      Case "delaytemp2", "sysstringa": DestLoc = 26
      Case "syswaittempus", "syswaittemp10us", "syswaittempm": DestLoc = 27
      Case "sysstringa_h": DestLoc = 27
      Case "syswaittempus_h", "syswaittemph", "syscalctempb", "sysbytetempb", "syswordtempb", "sysintegertempb", "syslongtempb", "sysstringb": DestLoc = 28
      Case "syswaittempms", "syscalctempb_h", "syswordtempb_h", "sysintegertempb_h", "syslongtempb_h", "sysstringb_h": DestLoc = 29
      Case "syswaittempms_h", "syscalctempb_u", "syslongtempb_u", "sysdivmultb", "sysreada": DestLoc = 30
      Case "syswaittemp10ms", "syswaittemps", "syscalctempb_e", "syslongtempb_e": DestLoc = 31
      Case "sysdivmultb_h", "sysreada_h": DestLoc = 31

    End Select
  End If

  Return DestLoc
  
  End Function

Function GetSysVar(VarName As String) As SysVarType Pointer
  'Look up system variable in hash map
  Return CPtr(SysVarType Pointer, HashMapGet(SysVars, VarName))  
  End Function

Function GetSysVarAliasName( Lookup as String ) As String
  'Look up system alias in hash map, if the retrun string has leadng "ALIAS_" then remove
  'This enables AVR DX return lookups
  
  Dim As LinkedListElement Pointer TempList, CurrItem
  Dim As SysVarType Pointer SysVar
  Dim As Integer TempVar
  Dim As Integer Address
  Dim As String ReturnString, Bits

  'Remove BITs as bits are not part of the ALIAS
  Bits = ""
  If Instr( Lookup, ".") > 0 Then
    Bits = Trim(Mid(Lookup, InStr(Lookup, ".") ))
    Lookup = Trim(Left(Lookup, InStr(Lookup, ".") - 1))
  End If

  If GetSysVar( Lookup ) = 0 Then Return ""
  

  Address = GetSysVar( Lookup )->location

  TempList = HashMapToList(SysVars, -1)
  CurrItem = TempList->Next
  Do While CurrItem <> 0
    SysVar = CurrItem->MetaData
    If SysVar->AVRAlias = -1 Then
      ' print "ADDRESS:",address , CurrItem->Value, SysVar->Name , SysVar->Location, SysVar->AVRAlias
      If SysVar->Location = Address and Trim(SysVar->Name) <> Trim(Lookup) Then
        ReturnString = SysVar->Name
        If Left( SysVar->Name, 6 ) = "ALIAS_" Then 
          ReturnString =  Mid( SysVar->Name, 7)
        End If
        ' print "2 Return Alias" + ReturnString+Bits
        Return ReturnString+Bits
        Exit Function
      End If
    End If
    CurrItem = CurrItem->Next
  Loop
  Return ""  
  End Function

Function GetSysVarName(Address as Integer) As String
  'Look up system variable in hash map
  
  Dim As LinkedListElement Pointer TempList, CurrItem
  Dim As SysVarType Pointer SysVar
  Dim As Integer TempVar

  TempList = HashMapToList(SysVars, -1)
  CurrItem = TempList->Next
  Do While CurrItem <> 0
    SysVar = CurrItem->MetaData
    ' print "ADDRESS:",address, SysVar->Location , SysVar->Name , SysVar->Location = Address
    If SysVar->Location = Address and SysVar->AVRAlias = 0 Then
      'print "Return" + SysVar->Name
      Return SysVar->Name
    End If

    CurrItem = CurrItem->Next
  Loop
  Return ""  
  End Function


Function GetSub(Origin As String) As String
  Dim As String Temp
  Dim As Integer T

  IF Origin = "ALL" OR Origin = "" Then Return "ALL"
  Temp = UCase(Trim(Mid(Origin, INSTR(Origin, "S") + 1)))
  IF INSTR(Temp, "D") <> 0 THEN Temp = Left(Temp, INSTR(Temp, "D") - 1)
  T = VAL(Temp)
  IF T = 0 THEN Return "ALL"

  Temp = Trim(Subroutine(T)->Name)
  IF INSTR(Temp, "(") <> 0 Then Temp = Left(Temp, INSTR(Temp, "(") - 1)
  IF INSTR(Temp, " ") <> 0 Then Temp = Left(Temp, INSTR(Temp, " ") - 1)
  IF Temp = "" THEN Temp = "ALL"
  GetSub = Trim(UCase(Temp))  
  End Function

Function GetSubFullName(SubIndex As Integer) As String

  Dim As String SubNameOut
  Dim As SubType Pointer CompSub
  CompSub = Subroutine(SubIndex)

  'Get the name that the Sub will receive when written to the asm
  If CompSub->Overloaded Then
    SubNameOut = CompSub->Name + Str(SubIndex)
  Else
    SubNameOut = CompSub->Name
  End If
  If CompSub->IsFunction Then
    SubNameOut = "FN_" + SubNameOut
  End If

  Return SubNameOut
  End Function

Function GetSubID(Origin As String) As Integer
  Dim As String Temp

  If Origin = "ALL" OR Origin = "" Then Return 0
  Temp = UCase(Trim(Mid(Origin, INSTR(Origin, "S") + 1)))
  IF INSTR(Temp, "D") <> 0 THEN Temp = Left(Temp, INSTR(Temp, "D") - 1)
  Return VAL(Temp)
  
  End Function

Function GetSubSig(CurrentSub As SubType) As String
  Dim As String OutData, NewData
  Dim As Integer PD

  OutData = ""
  With CurrentSub
    For PD = 1 to .ParamCount
      With .Params(PD)
        NewData = ""
        Select Case UCASE(.Type)
          Case "BIT", "BYTE", "WORD", "INTEGER", "LONG", "FLOAT", "STRING": NewData = UCase(.Type) + ":"
        End Select
        If .Default <> "" Then NewData = LCase(NewData)
        OutData += NewData
      End With
    Next
  End With

  Return OutData
  End Function

Function GetSubParam (ParamIn As String, ForceIn As Integer) As SubParam
  'Parse a text representation of a single Sub parameter, and output a
  'matching SubParam item
  'If ForceIn = -1, parameter will be treated as input

  Dim ParamOut As SubParam
  Dim As String TempData, ParamData

  ParamData = UCase(Trim(ParamIn))

  With ParamOut
    .Dir = 3
    .Default = ""

    'In/Out, OPTIONAL
    CheckStartAgain:
    'Print ParamData
    If Left(ParamData, 3) = "IN " Then
      .Dir = 1
      ParamData = Mid(ParamData, 4)
      Goto CheckStartAgain
    ElseIf Left(ParamData, 4) = "OUT " Then
      .Dir = 2
      ParamData = Mid(ParamData, 5)
      Goto CheckStartAgain
    ElseIf Left(ParamData, 9) = "OPTIONAL " Then
      .Default = Trim(Mid(ParamData, Instr(ParamData, "=") + 1))
      ParamData = Trim(Left(ParamData, Instr(ParamData, "=") - 1))
      ParamData = Trim(Mid(ParamData, 10))
      Goto CheckStartAgain
    'Optional is optional, might just have the default value
    ElseIf InStr(ParamData, "=") <> 0 Then
      .Default = Trim(Mid(ParamData, Instr(ParamData, "=") + 1))
      ParamData = Trim(Left(ParamData, Instr(ParamData, "=") - 1))
      Goto CheckStartAgain
    End If

    'Type
    If INSTR(ParamData, " AS ") <> 0 Then
      .Type = Mid(ParamData, Instr(ParamData, " AS ") + 4)
      ParamData = Left(ParamData, Instr(ParamData, " AS ") - 1)
    Else
      .Type = "BYTE"
    End If

    'Name
    .Name = Trim(ParamData)

    'Type detection for () and $
    If Right(.Name, 2) = "()" Then
      .Name = Left(.Name, Len(.Name) - 2)
      .Type = .Type + "()"
    ElseIf Right(.Name, 1) = "$" Then
      .Name = Left(.Name, Len(.Name) - 1)
      .Type = "STRING"
    End If

    'Force input?
    If ForceIn Then
      .Dir = 1
    End If

  End With

  Return ParamOut
  
  End Function

Function GetTool(ToolName As String) As ExternalTool Pointer
  'Returns a pointer to the tool named by ToolName, or 0 if nothing found

  If ToolName = "" Then Return 0

  Dim As Integer FindTool
  For FindTool = 1 To ToolCount
    If Tool(FindTool).Name = LCase(ToolName) Then
      Return @Tool(FindTool)
    End If
  Next

  Return 0
  End Function

Sub InitCompiler

  'Misc temp vars
  Dim As String Temp, DataSource, MessagesFile, LangMessagesFile, LangName, MsgName, MsgVal
  Dim As String SettingsFile(20), CurrentTag, ParamUpper, LeftThree, NewFileName
  Dim As Integer T, Block, CD, FCB, IniNotSet, SettingsFileMode, FindTool, FindMsg
  Dim As Integer SettingsFiles, CurrSettingsFile

  Dim As Integer PICAsmNotSet, AsmNotSet, ProgNotSet, OutNotSet, PresNotSet, VbsNotSet, WarnErrorNotSet
  Dim As Integer PauseNotSet, ReportNotSet, FlashOnlyNotSet

  'Detect GCBASIC install directory
  ID = ExePath
  If ID = "" Or ID = "." THEN
    ID = CURDIR
    #IFDEF __FB_UNIX__
      If Right(ID, 1) = "/" Then ID = Left(ID, Len(ID) - 1)
    #ELSE
      If Right(ID, 1) = "\" Then ID = Left(ID, Len(ID) - 1)
    #ENDIF
  End If

  'Clear parameters
  OFI = ""
  AsmExe = ""
  PrgExe = ""
  HexAppend = ""
  AsmTool = 0
  PrgTool = 0
  VBS = 0
  PauseOnErr = 1
  WarningsAsErrors = 0
  MuteBanners = -1
  MuteDonate = 0
  ExtendedVerboseMessages= 0
  PreserveMode = 0
  PauseAfterCompile = 0
  ShowProgressCounters = -1
  FlashOnly = 0
  SkipHexCheck = 0
  GCGB = 0
  CompReportFormat = "html"
  PauseTimeout = -1

  SettingsFile(1) = "gcbasic.ini"
  SettingsFiles = 1
  AsmNotSet = -1
  PICAsmNotSet = -1
  ProgNotSet = -1
  OutNotSet = -1
  PresNotSet = -1
  VbsNotSet = -1
  PauseNotSet = -1
  WarnErrorNotSet = -1
  IniNotSet = -1
  ReportNotSet = -1
  FlashOnlyNotSet = -1
  OutPutConfigOptions = -1

  'Read parameters
  CD = 1
  Do While COMMAND(CD) <> ""
    DataSource = COMMAND(CD)
    ParamUpper = Ucase(COMMAND(CD))
    LeftThree = Left(ParamUpper, 3)

    'Simple options
    'Verbose mode
    If ParamUpper = "/V" Or ParamUpper = "-V" Then
      VBS = 1
      VbsNotSet = 0

    'Show licence
    ElseIf ParamUpper = "/L" Or ParamUpper = "-L" Then
      ShowBlock "License"
      END

    'Pause on errors?
    ElseIf ParamUpper = "/NP" Or ParamUpper = "-NP" Then
      PauseOnErr = 0
      PauseNotSet = 0

    ElseIf ParamUpper = "/WX" Or ParamUpper = "-WX" Then
      WarningsAsErrors = -1
      WarnErrorNotSet = 0

    ElseIf ParamUpper = "/M" Or ParamUpper = "-M" Then
      MuteBanners = 0

    ElseIf ParamUpper = "/F" Or ParamUpper = "-F" Then
      FlashOnly = -1
      FlashOnlyNotSet = 0

    ElseIf ParamUpper = "/FO" Or ParamUpper = "-FO" Then
      SkipHexCheck = -1
      FlashOnly = -1
      FlashOnlyNotSet = 0

    'Great Cow Graphical BASIC Mode?
    '(Alters the error listing format)
    ElseIf ParamUpper = "/GCGB" Then
      GCGB = 1

    ElseIf ParamUpper = "/CLS" Then  
      CLS
    ElseIf ParamUpper = "/VERSION" Then
      Print Version
      End

    'Complex options
    'Allow verbose mode to be turned off
    ElseIf LeftThree = "/V:" Or LeftThree = "-V:" Then
      VBS = PrefIsYes(Mid(ParamUpper, 4), 1)
      VbsNotSet = 0

    'Allow flash only to be turned off
    ElseIf LeftThree = "/F:" Or LeftThree = "-F:" Then
      FlashOnly = PrefIsYes(Mid(ParamUpper, 4))
      FlashOnlyNotSet = 0

    'Settings file
    ElseIf LeftThree = "/S:" Or LeftThree = "-S:" Then
      SettingsFile(1) = Mid(DataSource, 4)
      IniNotSet = 0

    'Assembler command
    ElseIf LeftThree = "/A:" Or LeftThree = "-A:" Then


     AsmExe = Trim(Mid(DataSource, 4))
     if instr ( AsmExe, "PIC-AS") = 0 then
        'as was code
        AsmParams = ""
        If Left(AsmExe, 1) = Chr(34) Then
          For FCB = 2 to Len(AsmExe)
            If Mid(AsmExe, FCB, 1) = Chr(34) Then
              AsmParams = Trim(Mid(AsmExe, FCB + 1, 1))
              AsmExe = Trim(Left(AsmExe, FCB))
            End If
          Next
        ElseIf Instr(AsmExe, " ") <> 0 Then
          AsmParams = Mid(AsmExe, Instr(AsmExe, " ") + 1)
          AsmExe = Left(AsmExe, Instr(AsmExe, " ") - 1)
        End If

        AsmNotSet = 0
        'Do While INSTR(MakeASM, Chr(34)) <> 0: Replace MakeASM, Chr(34), "": Loop
      else

        'PIC-AS Assembler command
        AsmExe = Trim(Mid(DataSource, 4))
        AsmParams = ""
        If Left(AsmExe, 1) = Chr(34) Then
          For FCB = 2 to Len(AsmExe)
            If Mid(AsmExe, FCB, 1) = Chr(34) Then
              AsmParams = Trim(Mid(AsmExe, FCB + 1, 1))
              AsmExe = Trim(Left(AsmExe, FCB))
            End If
          Next
        ElseIf Instr(AsmExe, " ") <> 0 Then
          AsmParams = Mid(AsmExe, Instr(AsmExe, " ") + 1) +  " > "+chr(34)+"%Fn_NoExt%.err"+chr(34)
          AsmExe = Left(AsmExe, Instr(AsmExe, " ") - 1)
        End If

        AFISupport = 1
        PICASMNotSet = 0

      End if




    'Programmer command
    ElseIf LeftThree = "/P:" Or LeftThree = "-P:" Then
      PrgExe = Trim(Mid(DataSource, 4))
      PrgParams = ""
      PrgDir = ""
      If Left(PrgExe, 1) = Chr(34) Then
        For FCB = 2 to Len(PrgExe)
          If Mid(PrgExe, FCB, 1) = Chr(34) Then
            PrgParams = Trim(Mid(PrgExe, FCB + 1, 1))
            PrgExe = Trim(Left(PrgExe, FCB))
          End If
        Next
      ElseIf Instr(PrgExe, " ") <> 0 Then
        PrgParams = Mid(PrgExe, Instr(PrgExe, " ") + 1)
        PrgExe = Left(PrgExe, Instr(PrgExe, " ") - 1)
      End If
      ProgNotSet = 0
      'Do While INSTR(SendToPIC, Chr(34)) <> 0: Replace SendToPIC, Chr(34), "": Loop

    'Output filename
    ElseIf LeftThree = "/O:" Or LeftThree = "-O:" Then
      OFI = Mid(DataSource, 4)
      OutNotSet = 0
      'Do While INSTR(OFI, Chr(34)) <> 0: Replace OFI, Chr(34), "": Loop

    'Make hex mode
    ElseIf LeftThree = "/H:" Or LeftThree = "-H:" Then
      Temp = Right(ParamUpper, 1)
      IF Temp = "Y" OR Temp = "1" Then MakeHexMode = 1
      IF Temp = "N" OR Temp = "0" Then MakeHexMode = 0

    'Preserve mode
    ElseIf LeftThree = "/K:" Or LeftThree = "-K:" Then
      Temp = Right(ParamUpper, 1)
      IF Temp = "C" OR Temp = "1" Then PreserveMode = 1
      IF Temp = "A" OR Temp = "2" Then PreserveMode = 2
      If Temp = "L" OR Temp = "3" Then PreserveMode = 3
      PresNotSet = 0

    'Report mode
    ElseIf LeftThree = "/R:" Or LeftThree = "-R:" Then
      CompReportFormat = LCase(Mid(DataSource, 4))
      ReportNotSet = 0

    ElseIf LeftThree = "/CP" Or LeftThree = "-CP" Then
      OutPutConfigOptions = 0

    'Deprecated options
    'Clear screen
    ElseIf ParamUpper = "/NC" or ParamUpper = "-NC" Then
      'CLS

    'Compiler directory
    ElseIf LeftThree = "/D:" Or LeftThree = "-D:" Then

    'Input filename
    Else
      FI = ShortName(DataSource)

    End If

    CD += 1
  Loop

  'Read settings file
  CurrSettingsFile = 0
  Do
    CurrSettingsFile += 1

    #IFDEF __FB_UNIX__
      If Instr(SettingsFile(CurrSettingsFile), "/") = 0 Then SettingsFile(CurrSettingsFile) = ID + "/" + SettingsFile(CurrSettingsFile)
    #ELSE
      If Instr(SettingsFile(CurrSettingsFile), "\") = 0 Then SettingsFile(CurrSettingsFile) = ID + "\" + SettingsFile(CurrSettingsFile)
    #ENDIF
    If Dir(SettingsFile(CurrSettingsFile)) = "" Then
      If VBS = 1 And IniNotSet = 0 Then Print "Cannot find settings file " + SettingsFile(CurrSettingsFile)
    Else
      Open SettingsFile(CurrSettingsFile) For Input As #1
      globalSettingsFile = SettingsFile(CurrSettingsFile)
      'Decide mode to read file in
      SettingsFileMode = 0
      Do While Not Eof(1)
        Line Input #1, DataSource
        DataSource = Trim(DataSource)
        If Left(DataSource, 1) = "[" And Right (DataSource, 1) = "]" Then
          SettingsFileMode = 1
          Exit Do
        End If
      Loop
      Seek #1, 1
      'Read actual file
      CurrentTag = ""
      If SettingsFileMode = 0 Then CurrentTag = "gcbasic"
      Do While Not EOF(1)
        Line Input #1, DataSource
        Do While InStr(DataSource, Chr(9)) <> 0: Replace DataSource, Chr(9), " ": Loop
        DataSource = Trim(DataSource)

        If DataSource <> "" And Left(DataSource, 1) <> "'" Then

          If Left(DataSource, 8) = "include " Then
            NewFileName = Trim(Mid(DataSource, 9))

            'Replace %APPDATA% with APPDATA environment var
            NewFileName = ReplaceToolVariables(NewFileName)

            Dim As Integer CheckFile, FileFound
            FileFound = 0
            For CheckFile = 1 To SettingsFiles
              If SettingsFile(CheckFile) = NewFileName Then FileFound = -1: Exit For
            Next
            If Not FileFound Then
              SettingsFiles += 1
              SettingsFile(SettingsFiles) = NewFileName
            End If

          ElseIf SettingsFileMode = 1 And Left(DataSource, 1) = "[" And Right(DataSource, 1) = "]" Then
            CurrentTag = LCase(Mid(DataSource, 2, Len(DataSource) - 2))

            If Left(CurrentTag, 4) = "tool" Then
              ToolCount += 1
              With Tool(ToolCount)
                If InStr(CurrentTag, "=") <> 0 Then
                  .Name = LCase(Trim(Mid(CurrentTag, InStr(CurrentTag, "=") + 1)))
                Else
                  .Name = ""
                End If
                .Type = ""
                .Cmd = ""
                .Params = ""
                .ExtraParams = 0
                .ProgConfig = ""
                .ProgOptions = ""
                .UseIf = ""
              End With
            EndIf

          Else
            MsgName = LCase(Trim(Left(DataSource, INSTR(DataSource, "=") - 1)))
            MsgVal = Trim(Mid(DataSource, INSTR(DataSource, "=") + 1))

            If CurrentTag = "gcbasic" Then
              Select Case MsgName
                Case "assembler"
                If AsmNotSet Then
                  AsmExe = MsgVal
                  SelectedAssembler = MsgVal
                  if instr( ucase(MsgVal), "PIC-AS") > 0 then
                          AFISupport = 1
                  End If
                  Replace ( SelectedAssembler, "-", "" )
                End if

                Case "conditionaldebugfile"
                  Conditionaldebugfile = MsgVal
                  if Len(Conditionaldebugfile) > 0 Then
                    CDFSupport = 1
                  End if

                Case "columnwidth"
                  Columnwidth = Val(Trim(MsgVal))
                  if Columnwidth = 0 Then
                    Columnwidth = 77
                  End if



                Case "assemblerparams"
                If AsmNotSet Then AsmParams = MsgVal

                Case "programmer"
                If ProgNotSet Then PrgExe = MsgVal

                Case "programmerparams"
                If ProgNotSet Then PrgParams = MsgVal

                Case "output"
                If OutNotSet Then OFI = MsgVal

                Case "preserve"
                If PresNotSet Then
                  Select Case LCase(Left(MsgVal, 1))
                    Case "c", "1": PreserveMode = 1
                    Case "a", "2": PreserveMode = 2
                    Case "l", "3": PreserveMode = 3
                  End Select
                End If

                Case "workingdir"
                If LCase(MsgVal) = "tempdir" Or LCase(MsgVal) = "instdir" Then
                  MsgVal = "%" + MsgVal + "%"
                End If
                MsgVal = ReplaceToolVariables(MsgVal)
                ChDir MsgVal

                Case "verbose"
                If VbsNotSet Then
                  VBS = PrefIsYes(MsgVal, 1)
                End If

                Case "pauseonerror"
                If PauseNotSet Then
                  PauseOnErr = PrefIsYes(MsgVal, 1)
                End If

                Case "showprogresscounters":
                ShowProgressCounters = PrefIsYes(MsgVal)

                Case "warningsaserrors"
                If WarnErrorNotSet Then
                  WarningsAsErrors = PrefIsYes(MsgVal)
                End If

                Case "mutebanners"
                  MuteBanners = NOT PrefIsYes(MsgVal)

                Case "mutedonateplyx"
                  MuteDonate = PrefIsYes(MsgVal)

                Case "evbs"
                  if PrefIsYes(MsgVal, 1 ) = 1   Then
                    ExtendedVerboseMessages= -1
                  End if

                Case "laxsyntax"
                  if PrefIsYes(MsgVal, 1 ) = 1   Then
                    laxsyntax = -1
                  End if

                Case "picasdebug"
                  if PrefIsYes(MsgVal, 1 ) = 1   Then
                    PICASdebug = true
                  End if
                
                Case "methodstructuredebug"
                  if PrefIsYes(MsgVal, 1 ) = 1   Then
                    methodstructuredebug = true
                  End if

                Case "floatcapability"
                    If floatcapability = 0 Then  'so, not set by #OPTION FLOATS 
                      floatcapability = val(msgval)
                      VersionSuffix = Str(floatcapability)
                    End if

                Case "compilerdebug"
                    compilerdebug = val(msgval)

                Case "overridelowleveldatfileextextension"
                    overridelowleveldatfileextextension = val(msgval)
                
                Case "overridelowleveldatfileextextensionmessage"
                    overridelowleveldatfileextextensionmessage = val(msgval)
                
                Case "overridetestdatfilemessage"
                    overridetestdatfilemessage = val(msgval)

                Case "datfileinspection"
                  if PrefIsYes(MsgVal, 1 ) = 1   Then
                    DATfileinspection = true
                  else
                    DATfileinspection = false
                  End if                

                Case "nosummary"
                  if PrefIsYes(MsgVal, 1 ) = 1   Then
                    NoSummary = -1
                  End if

                Case "flashonly"
                If FlashOnlyNotSet Then
                  FlashOnly = PrefIsYes(MsgVal)
                End If

                Case "pauseaftercompile"
                  PauseAfterCompile = PrefIsYes(MsgVal)

                Case "pausetimeout"
                  PauseTimeout = Val(MsgVal)

                Case "language"
                  LangName = MsgVal

                Case "hexappendgcbmessage"
                  Select Case LCase(Left(MsgVal, 1))
                    Case "Y","y", "1": HexAppend = ";HEX file generated by GCBASIC"
                  End Select


                Case "report"
                  If ReportNotSet Then
                    CompReportFormat = LCase(MsgVal)
                  End If

              End Select
            ElseIf CurrentTag = "libaries" Then
              Select Case MsgName
                Case "disabled"
                  IgnoreSourceFiles = IgnoreSourceFiles + 1
                  IgnoreSourceFile(IgnoreSourceFiles).FileName = MsgVal
                  IgnoreSourceFile(IgnoreSourceFiles).Ignore = -1
                Case "enabled"
                  'do nothing
              End Select
            ElseIf CurrentTag = "toolvariables" Then
              Dim As String Pointer TempStr
              TempStr = Callocate(SizeOf(String))
              *TempStr = MsgVal
              LinkedListInsert(ToolVariables, MsgName)->MetaData = TempStr

            ElseIf CurrentTag= "patch=asm2picas" Then
              'create patches from

              Dim localCharPosition as Integer
              'strip off first quote
              localCharPosition = instr( DataSource, chr(34) )
              DataSource = Mid( DataSource, localCharPosition + 1 )

              localCharPosition = instr( DataSource, chr(34) )
              MsgName = left ( DataSource, localCharPosition - 1 )

              Patches( patchCounter ).Source = ucase(MsgName)

              DataSource = mid ( DataSource , localCharPosition )

              localCharPosition = instr( DataSource, "=" )
              DataSource = Mid ( DataSource,localCharPosition + 1 )


              localCharPosition = instr( DataSource, chr(34) )  'find the next char
              DataSource = mid ( DataSource , localCharPosition + 1 )

              localCharPosition = instr( DataSource, chr(34) )  'find the next char
              MsgVal = mid ( DataSource ,1, localCharPosition -1 )
              Patches( patchCounter ).Destination = MsgVal
              patchCounter = patchCounter + 1

            ElseIf Left(CurrentTag, 4) = "tool" Then
              Select Case MsgName
                Case "name"
                  Tool(ToolCount).Name = LCase(MsgVal)
                Case "desc"
                  Tool(ToolCount).DispName = MsgVal
                Case "type"
                  Tool(ToolCount).Type = LCase(MsgVal)
                Case "command"
                  Tool(ToolCount).Cmd = MsgVal
                  if instr( ucase(MsgVal), "PIC-AS") > 0 then
                      if left( MsgVal, 1 )  =  chr(34) then MsgVal = mid( MsgVal, 1)
                      if right( MsgVal, 1 ) =  chr(34) then MsgVal = right( MsgVal, 1)
                      Tool(ToolCount).Cmd  = chr(34)+MsgVal+chr(34)  'PICASDriver
                  End if

                Case "params"
                  Tool(ToolCount).Params = MsgVal
                  if instr( ucase(Tool(ToolCount).Cmd), "PIC-AS") > 0 then
                        Tool(ToolCount).Params = MsgVal +  " > "+chr(34)+"%Fn_NoExt%.err"+chr(34)  'PICAsmParams
                  End if
                Case "workingdir"
                  Tool(ToolCount).WorkingDir = MsgVal
                Case "progconfig"
                  Tool(ToolCount).ProgConfig = MsgVal
                Case "progoptions"
                  Tool(ToolCount).ProgOptions = MsgVal
                Case "useif"
                  Tool(ToolCount).UseIf = MsgVal
                Case Else
                  With Tool(ToolCount)
                    If .ExtraParams < 5 Then
                      .ExtraParams += 1
                      .ExtraParam(.ExtraParams, 1) = MsgName
                      .ExtraParam(.ExtraParams, 2) = MsgVal
                    End If
                  End With
              End Select


            End If
          End If
        End If
      Loop
      Close
    End If
  Loop While CurrSettingsFile < SettingsFiles

  'Read message list
  #IFDEF __FB_UNIX__
    MessagesFile = ID + "/messages.dat"
    LangMessagesFile = ID + "/messages-" + LCase(LangName) + ".dat"
  #ELSE
    MessagesFile = ID + "\messages.dat"
    LangMessagesFile = ID + "/messages-" + LCase(LangName) + ".dat"
  #ENDIF
  IF Dir(MessagesFile) = "" THEN
    PRINT "Cannot find " + MessagesFile
    Print
    PRINT "GCBASIC cannot operate without this file"
    If PauseOnErr = 1 THEN
      PRINT
      PRINT "Press any key to continue"
      Sleep
    END IF
    END
  END IF

  'Load localised messages file
  If LangName <> "" Then
    If Dir(LangMessagesFile) = "" Then
      Print "Could not find localised message file " + LangMessagesFile
    Else
      OPEN LangMessagesFile FOR INPUT AS #1
      Block = 0
      DO WHILE NOT EOF(1)
        LINE INPUT #1, DataSource
        DataSource = Trim(DataSource)
        IF Left(UCase(DataSource), 6) = "BLOCK " THEN
          Block = 1
        ElseIF Left(UCase(DataSource), 9) = "END BLOCK" THEN
          Block = 0
        ElseIF Left(DataSource, 1) = "'" THEN
          DataSource = ""
        ElseIF INSTR(DataSource, "=") <> 0 THEN
          MsgName = Left(DataSource, InStr(DataSource, "=") - 1)
          MsgVal = Mid(DataSource, INSTR(DataSource, "=") + 1)
          IF INSTR(MsgVal, Chr(34)) <> 0 THEN
            MsgVal = Mid(MsgVal, INSTR(MsgVal, Chr(34)) + 1)
            MsgVal = Left(MsgVal, INSTR(MsgVal, Chr(34)) - 1)
          END IF
          'Add message if it doesn't exist already
          MsgName = Trim(UCase(MsgName))
          T = 0
          For FindMsg = 1 To MSGC
            If MsgName = Messages(1, FindMsg) Then T = FindMsg: Exit For
          Next
          If T = 0 Then
            MSGC = MSGC + 1
            Messages(1, MSGC) = MsgName
            Messages(2, MSGC) = MsgVal
          End If
        END IF
      LOOP
      CLOSE #1
    End If
  End If

  OPEN MessagesFile FOR INPUT AS #1
  Block = 0
  DO WHILE NOT EOF(1)
    LINE INPUT #1, DataSource
    DataSource = Trim(DataSource)
    IF Left(UCase(DataSource), 6) = "BLOCK " THEN
      Block = 1
    ElseIF Left(UCase(DataSource), 9) = "END BLOCK" THEN
      Block = 0
    ElseIF Left(DataSource, 1) = "'" THEN
      DataSource = ""
    ElseIF INSTR(DataSource, "=") <> 0 THEN
      MsgName = Left(DataSource, INSTR(DataSource, "=") - 1)
      MsgVal = Mid(DataSource, INSTR(DataSource, "=") + 1)
      IF INSTR(MsgVal, Chr(34)) <> 0 THEN
        MsgVal = Mid(MsgVal, INSTR(MsgVal, Chr(34)) + 1)
        MsgVal = Left(MsgVal, INSTR(MsgVal, Chr(34)) - 1)
      END IF
      'Add message if it doesn't exist already
      MsgName = Trim(UCase(MsgName))
      T = 0
      For FindMsg = 1 To MSGC
        If MsgName = Messages(1, FindMsg) Then T = FindMsg: Exit For
      Next
      If T = 0 Then
        MSGC = MSGC + 1
        Messages(1, MSGC) = MsgName
        Messages(2, MSGC) = MsgVal
      End If
    END IF
  LOOP
  CLOSE #1

  'Message if no filename specified
  IF FI = "" THEN
    ShowBlock "NoPrompt"
    If PauseOnErr = 1 THEN
      WaitForKeyOrTimeout
    END IF
    END
  END IF

  'Decide name for output file if not specified
  'If name is specified, do not allow flashing only
  IF OFI = "" THEN
    OFI = FI
    For CD = LEN(OFI) TO 1 STEP -1
      IF Mid(OFI, CD, 1) = "." THEN OFI = Left(OFI, CD - 1): EXIT FOR
    Next

    CDF = OFI + ".cdf"
    AFI = OFI + ".s"
    OFI = OFI + ".asm"


  Else
    FlashOnly = 0
  END IF

  'Find directory of source file (used for relative include)
  ProgDir = CURDIR
  IF INSTR(FI, "\") <> 0 THEN
    FOR T = LEN(FI) TO 1 STEP -1
      IF Mid(FI, T, 1) = "\" Or Mid(FI, T, 1) = "/" Then EXIT FOR
    NEXT
    ProgDir = Left(FI, T - 1)
  END IF
  IF Right(ProgDir, 1) = "\" THEN ProgDir = Left(ProgDir, LEN(ProgDir) - 1)
  IF Right(ProgDir, 1) = "/" THEN ProgDir = Left(ProgDir, LEN(ProgDir) - 1)

  'Load file converters
  LoadConverters

  'Show version
  PRINT "GCBASIC (" + Version + ")"
  Print

  IF Dir(FI) = "" THEN
    PRINT
    PRINT Message("NoFile")
    If PauseOnErr = 1 THEN
      WaitForKeyOrTimeout
    END IF
    END
  END IF

  'Start Compile
  IF VBS = 0 Then   PRINT Message("CompilingShortname") Else  PRINT Message("Compiling")
  
  End Sub

Function IsArray (VarNameIn As String, CurrSub As SubType Pointer) As Integer
  IsArray = 0
  Dim As VariableType Pointer FoundVar
  Dim As String VarName
  'If () after variable name, remove and check variable
  VarName = Trim(UCase(VarNameIn))
  If Right(VarName, 2) = "()" Then
    VarName = RTrim(Left(VarName, Len(VarName) - 2))
  End If

  'Array var?
  FoundVar = HashMapGet(@(CurrSub->Variables), VarName)
  If FoundVar <> 0 Then
    If FoundVar->Size > 1 Then
      Return -1
    End If
  End If

  'Main sub
  FoundVar = HashMapGet(@(Subroutine(0)->Variables), VarName)
  If FoundVar <> 0 Then
    If FoundVar->Size > 1 Then
      Return -1
    End If
  End If
  
  End Function

Function IsNonBanked(Location As Integer) As Integer
  'Returns -1 if the location does not require banking
  'PIC only, return true on all other chips

  Dim As Integer CurrBank

  If ModePIC Then
    'Check all shared banks
    For CurrBank = 1 To NoBankLocs
      With NoBankLoc(CurrBank)
        'If the location is in a shared bank, return true
        If Location >= .StartLoc And Location <= .EndLoc Then
          Return -1
        End If
      End With
    Next
    Return 0

  Else
    Return -1
  End If
  
  End Function

Function IsInAccessBank(VarNameIn As String) As Integer
  'Check if a specified variable is located in the access bank
  Dim As Integer FindVar
  Dim As String VarName
  Dim As SysVarType Pointer FoundVar

  '18F only at this stage 
  ' If statement below remove at build 1446
  ' If ChipFamily <> 16 Then Return 0

  VarName = UCase(Trim(VarNameIn))

  'Check for SFRs and Registers
  'Need to get location, then check if it is a non-banked location
  'Search system variable list
  FoundVar = GetSysVar(VarName)
  If FoundVar <> 0 Then
    Return IsNonBanked(FoundVar->Location)
  End If

  'Check if the variable being accessed is a SFR
  If IsRegister(VarNameIn) Then
    Return -1
  End If
  Return 0
  End Function

Function IsIOPinName(PinNameIn As String) As Integer
  Dim PinName As String
  PinName = PinNameIn

  'Checks if PinName is that of an IO pin.
  'Format: [RP][A-Z][0-7] Or GP[0-7]
  If Len(PinName) <> 3 Then Return 0
  PinName = UCase(PinName)

  If Left(PinName, 1) = "R" Or Left(PinName, 1) = "P" Then
    If Mid(PinName, 2, 1) < "A" Or Mid(PinName, 2, 1) > "Z" Then Return 0
    If InStr("01234567", Mid(PinName, 3, 1)) = 0 Then Return 0
  Else
    If Left(PinName, 2) <> "GP" Then Return 0
    If InStr("01234567", Mid(PinName, 3, 1)) = 0 Then Return 0
  End If

  Return -1
  End Function

Function IsIORegDX (RegNameIn As String) As Integer
  Dim RegName As String
  Dim As Integer SD
  Dim As SysVarType Pointer FoundVar

  'Check if a register is in the IO space
  RegName = TRIM(UCASE(RegNameIn))
  'Only check the register name not the bit
  If Instr( RegName, ".") > 0 Then
    RegName = Left(RegName, InStr(RegName, ".") - 1)
  End If

  'Search, return true if found
  FoundVar = GetSysVar(RegName)
  If FoundVar <> 0 Then
    'print RegName, FoundVar->AVRAlias, FoundVar->Location, FoundVar->Name
    If FoundVar->AVRAlias = -1 Then
      Return -1
    End If
  End If

  Return 0
  End Function

Function IsIOReg (RegNameIn As String) As Integer
  Dim RegName As String
  Dim As Integer SD
  Dim As SysVarType Pointer FoundVar

  'Check if a register is in the IO space
  RegName = TRIM(UCASE(RegNameIn))

  'Search, return true if found
  FoundVar = GetSysVar(RegName)
  If FoundVar <> 0 Then
    If ModePIC Then
      Return -1
    ElseIf ModeAVR Then
      If FoundVar->Location > 63 Then
        Return 0
      Else
        Return -1
      End If
    End If
  End If

  'Check aliases
  'May have an alias to an IO register
  Dim As String Source, Temp
  Dim As Integer CurrSub
  Dim As VariableType Pointer FoundUserVar
  Source = Trim(UCase(RegName))
  Temp = ""
  For CurrSub = 0 To SBC
    FoundUserVar = HashMapGet(@(Subroutine(CurrSub)->Variables), Source)
    If FoundUserVar <> 0 THEN
      'Have found an alias?
      Temp = FoundUserVar->Alias
      If Temp <> "" Then
        If InStr(Temp, ",") <> 0 Then Temp = Trim(Left(Temp, InStr(Temp, ",") - 1))
        If IsIOReg(Temp) Then
          Return -1
        End If
      End If
    End If

  Next

  Return 0
  End Function

Function IsLowIOReg (RegNameIn As String) As Integer
  Dim RegName As String
  Dim As Integer SD
  Dim As SysVarType Pointer FoundVar

  'Check if a register is in the IO space
  RegName = TRIM(UCASE(RegNameIn))

  'Search, return true if found
  FoundVar = GetSysVar(RegName)
  If FoundVar <> 0 Then
    If ModePIC Then
      Return -1
    ElseIf ModeAVR Then
      If FoundVar->Location > 31 Then
        Return 0
      Else
        Return -1
      End If
    End If
  End If

  'Check aliases
  'May have an alias to a low IO register
  Dim As String Source, Temp
  Dim As Integer CurrSub
  Dim As VariableType Pointer FoundUserVar
  Source = Trim(UCase(RegName))
  Temp = ""
  For CurrSub = 0 To SBC
    FoundUserVar = HashMapGet(@(Subroutine(CurrSub)->Variables), Source)
    If FoundUserVar <> 0 THEN
      'Have found an alias?
      Temp = FoundUserVar->Alias
      If Temp <> "" Then
        If InStr(Temp, ",") <> 0 Then Temp = Trim(Left(Temp, InStr(Temp, ",") - 1))
        Return IsLowIOReg(Temp)
      End If
    End If

  Next

  Return 0
  End Function

Function IsLowRegister(VarName As String) As Integer
  'Note, this can only be run accurately after AllocateRAM has run
  'AVR only
  'Will return true if register is low (r0 to r15 inclusive)
  Dim As String RealName
  Dim As Integer CurrItem, RegNo

  If Not ModeAVR Then Return 0

  'If we have an alias, get the real name
  RealName = UCase(VarName)
  SearchAliasAgain:
  For CurrItem = 1 To FALC
    If RealName = FinalAliasList(CurrItem).Name Then
      RealName = FinalAliasList(CurrItem).Value
      GoTo SearchAliasAgain 'May have a nested alias
    End If
  Next
  RealName = UCase(RealName)

  'Is it low?
  For CurrItem = 1 To FRLC
    If RealName = FinalRegList(CurrItem).Name Then
      RegNo = Val(Mid(FinalRegList(CurrItem).Value, 2))
      If RegNo < 16 Then
        Return -1
      Else
        Return 0
      End If
    End If
  Next

  'Default to most restrictive but safest option
  Return -1
  End Function

Function IsRegister (VarName As String) As Integer
  Dim As String Temp, Source, SourceLowByte
  Dim As Integer CurrSub, MinAliasSize
  Dim As VariableType Pointer FoundVar
  Dim As VariableListElement Pointer FoundFinalVar

  'System vars that are always registers
  'SysTemp vars are only registers on AVR, no room to be registers on PIC
  If ModeAVR Then
    If IsSysTemp(VarName) Then Return -1
    IF UCase(VarName) = "SYSVALUECOPY" Then Return -1
    If UCase(VarName) = "SYSBITTEST" Then Return -1
  End If
  If ModePIC Then
    If UCase(VarName) = "SYSW" Then Return -1
    If UCase(VarName) = "SYSSTATUS" Then Return -1
  End If
  IF UCase(Left(VarName, 11)) = "SYSCALCTEMP" Then Return -1

  IF UCase(Left(VarName, 11)) = "SYSBYTETEMP" And InStr("ABXabx", Mid(VarName, 12, 1)) <> 0 Then Return -1
  IF UCase(Left(VarName, 11)) = "SYSWORDTEMP" And InStr("ABXabx", Mid(VarName, 12, 1)) <> 0 Then Return -1
  IF UCase(Left(VarName, 14)) = "SYSINTEGERTEMP" And InStr("ABXabx", Mid(VarName, 15, 1)) <> 0 Then Return -1
  IF UCase(Left(VarName, 11)) = "SYSLONGTEMP" And InStr("ABXabx", Mid(VarName, 12, 1)) <> 0 Then Return -1

  IF UCase(Left(VarName, 10)) = "SYSDIVMULT" Then Return -1
  IF UCase(Left(VarName, 11)) = "SYSWAITTEMP" Then Return -1
  IF UCase(Left(VarName, 11)) = "SYSWAITTEMP" OR UCase(Left(VarName, 9)) = "DELAYTEMP" Then Return -1
  If UCase(VarName) = "SYSREADA" Or UCase(VarName) = "SYSREADA_H" Then Return -1
  If UCase(Left(VarName, 9)) = "SYSSTRING" Then
    Temp = UCase(Mid(VarName, 10))
    If Temp = "A" Or Temp = "A_H" Or Temp = "B" Or Temp = "B_H" Or Temp = "LENGTH" Then
      Return -1
    End If
    Return 0
  End If
  IF UCase(VarName) = "SYSDIVLOOP" Then Return -1
  IF UCase(VarName) = "SYSSIGNBYTE" Then Return -1

  'Is location known and in access bank?
  If ModePIC And FinalVarList <> 0 Then
    FoundFinalVar = HashMapGet(FinalVarList, UCase(VarName))
    If FoundFinalVar <> 0 Then
      If Val(FoundFinalVar->Value) = 0 And FoundFinalVar->Value <> "0" Then
        Return 0
      Else
        Return IsNonBanked(Val(FoundFinalVar->Value))
      End If
    End If
  End If

  'User defined register vars
  'If it's a register in one sub, it will be in all
  Source = Trim(UCase(VarName))
  Temp = ""
  For CurrSub = 0 To SBC
    FoundVar = HashMapGet(@(Subroutine(CurrSub)->Variables), Source)
    IF FoundVar <> 0 THEN
      Temp = FoundVar->Pointer
      If Left(Temp, 8) = "REGISTER" Then
        Return -1
      End If
    END IF
  Next

  'Check aliases
  'May have an alias to a register
  Source = Trim(UCase(VarName))
  SourceLowByte = Source
  MinAliasSize = 1
  If Right(Source, 2) = "_H" Then
    SourceLowByte = Left(Source, Len(Source) - 2)
    MinAliasSize = 2
  ElseIf Right(Source, 2) = "_U" Or Right(Source, 2) = "_E" Then
    SourceLowByte = Left(Source, Len(Source) - 2)
    MinAliasSize = 4
  End If
  Temp = ""
  For CurrSub = 0 To SBC
    FoundVar = HashMapGet(@(Subroutine(CurrSub)->Variables), SourceLowByte)

    If FoundVar <> 0 THEN
      Temp = FoundVar->Alias
      'Have found an alias?
      If Temp <> "" Then
        If InStr(Temp, ",") <> 0 Then Temp = Trim(Left(Temp, InStr(Temp, ",") - 1))
        If Temp <> Source Then
          Return IsRegister(Temp)
        Else
          'Alias to self
          Return 0
        End If
      End If
    End If

  Next

  Return 0
  End Function

Function IsString (InData As String, CurrSub As SubType Pointer) As Integer
  IsString = 0
  Dim As Integer PD

  'String constant?
  IF INSTR(InData, "$") <> 0 OR INSTR(InData, ";STRING") <> 0 THEN Return -1

  'String var?
  If TypeOfVar(InData, CurrSub) = "STRING" Then Return -1
  END Function

Function IsUnaryOp (InData As String) As Integer

  If InData = "-" Then Return -1
  If InData = "!" Then Return -1
  Return 0
  
  End Function

Sub LoadConverters
  'Loads file format converters
  Dim As String SaveDir, ConvDir, CurrFile, InLine, InName, InVal
  Dim As Integer f

  'Change to directory
  SaveDir = CurDir
  #Ifdef __FB_UNIX__
    ConvDir = ID + "/converters"
  #ELSE
    ConvDir = ID + "\converters"
  #EndIf
  If ChDir(ConvDir) = 0 Then
    'Read each .ini file in directory
    CurrFile = Dir("*.ini")
    Do While CurrFile <> ""

      'Create new FileConverter
      FileConverters += 1
      With FileConverter(FileConverters)
        .Name = CurrFile
        .InFormats = 0
        .Params = ""
        .DeleteTarget = 0

        f = FreeFile
        Open CurrFile For Input As #f
        Do While Not Eof(f)
          Line Input #f, InLine
          If InStr(InLine, "=") <> 0 Then
            InName = LCase(Trim(Left(InLine, InStr(InLine, "=") - 1)))
            InVal = Trim(Mid(InLine, InStr(InLine, "=") + 1))

            'Process line
            If InName = "desc" Then
              .Desc = InVal
            ElseIf InName = "in" Then
              GetTokens(InVal, .InFormat(), .InFormats)
            ElseIf InName = "out" Then
              .OutFormat = InVal
            ElseIf InName = "exe" Then
              .ExeName = InVal
            ElseIf InName = "params" Then
              .Params = InVal
            ElseIf InName = "deletetarget" Then
              Select Case LCase(Left(InVal, 1))
                Case "y", "t", "1": .DeleteTarget = -1
                Case "n", "f", "0": .DeleteTarget = 0
              End Select
            End If
          End If
        Loop
        Close #f
      End With

      CurrFile = Dir
    Loop

  End If

  'Restore directory
  ChDir SaveDir
  
  End Sub

Function LocationOfSub (SubNameIn As String, SubSigIn As String, Origin As String = "", AllowVague As Integer = 0) As Integer

  'SubNameIn - name of Sub to find
  'SubSigIn - signature of Sub to find, leave blank to find all. If blank, may
  '           need to also set AllowVague to suppress error if multiple
  '           overloaded subs match name
  'Origin - Location Sub is called from. Used for error reporting. Optional
  'AllowVague - Set to -1 to suppress error if multiple overloaded subs are
  '             found and no signature provided. First matching Sub will be
  '             returned.

  'Sub name coming in can be just the name with parameters seperate or missing,
  'or can be output name (FN_READAD54, for example)

  'Returns:
  ' Location in Subroutines() of match
  ' 0 if no match
  ' -1 if overloaded, and couldn't determine which overload matches

  Dim As String SubName, SubSig, Temp, ParamTemp, ErrorTemp
  Dim As Integer T, FoundSameName, BestMatch, BestMatchLoc, ThisScore
  Dim As LinkedListElement Pointer SubElement

  SubName = UCase(LTrim(SubNameIn))
  SubSig = SubSigIn
  IF Left(SubName, 5) = "CALL " THEN SubName = Trim(Mid(SubName, 6))
  LocationOfSub = 0

  'If a var is being set, don't bother checking to see if the var is a sub
  IF Mid(SubName, INSTR(SubName, " ") + 1, 1) = "=" THEN EXIT FUNCTION

  'Remove any parameters from the name
  IF INSTR(SubName, "(") <> 0 THEN SubName = Left(SubName, INSTR(SubName, "(") - 1)
  IF INSTR(SubName, " ") <> 0 THEN SubName = Left(SubName, INSTR(SubName, " ") - 1)

  'Exit if SubName is a known, common command
  'This code is meant to save time, so no point in checking for common commands if they are not subs
  IF SubName = "IF" THEN EXIT FUNCTION
  IF SubName = "DO" THEN EXIT FUNCTION
  IF SubName = "SET" THEN EXIT FUNCTION
  IF SubName = "DIR" THEN EXIT FUNCTION
  IF SubName = "FOR" THEN EXIT FUNCTION
  IF SubName = "DIM" THEN EXIT FUNCTION
  IF SubName = "END" THEN EXIT FUNCTION
  IF SubName = "LOOP" THEN EXIT FUNCTION
  IF SubName = "NEXT" THEN EXIT FUNCTION
  IF SubName = "WAIT" THEN EXIT FUNCTION
  If Left(SubName, 5) = "ENDIF" Then Exit Function

  'Dodgy hack for SysReadString and SysCopyString to allow entry to middle of subroutine
  If SubName = "SYSREADSTRINGPART" Or SubName = "SYSCOPYSTRINGPART" Then
    SubName = Left(SubName, Len(SubName) - 4)
  End If

  'Returns the position of DataSource in SUBDATA()
  SubName = Trim(SubName)
  FoundSameName = 0

  'Prepare hash map for searching
  UpdateSubMap

  'Prepare to find best match
  BestMatch = 0
  BestMatchLoc = -2

  SubElement = HashMapGet(Subroutines, SubName)
  Do While SubElement <> 0
    T = SubElement->NumVal
    Temp = UCase(Trim(Subroutine(T)->Name))

    'Early exit if Sub not overloaded or vague allowed, return first found sub
    If Not Subroutine(T)->Overloaded Or AllowVague Then
      Return T
    End If

    'Is the supplied name the same name as in output code?
    'Full name will be unique, so have found the right one
    If SubName = UCase(GetSubFullName(T)) or Left(UCase(GetSubFullName(T)),2) = "FN" Then
      Return T
    End If

    'Record that a Sub was found with the right name
    FoundSameName = -1

    'Check if found Sub signature matches call signature
    If SubSig <> "" Then
      ThisScore = SubSigMatch(GetSubSig(*Subroutine(T)), SubSig)
      If ThisScore > BestMatch Then
        BestMatch = ThisScore
        BestMatchLoc = T
      End If

    'No call signature supplied
    Else
      'If this happens, there is a bug in the compiler
      'Or there is a Sub with no params

      'Found Sub has no parameters, so it matches
      If Subroutine(T)->ParamCount = 0 Then
        Return T

      'Sub does have parameters, but call does not so cannot find the best match
      Else
        ErrorTemp = Message("DuplicateSub")
        Replace ErrorTemp, "%sub%", SubName
        LogError ErrorTemp, Origin
        Return -1
      End If
    End If

    SubElement = SubElement->Next
  Loop

  'Found same name but none with valid sig, return -1 for error
  If BestMatchLoc = -2 And FoundSameName Then
    Return -1

  'Didn't find anything with the right name
  ElseIf FoundSameName = 0 Then
    Return 0

  'Found a match that may/should work
  Else
    Return BestMatchLoc
  End If
  
  End Function

Sub LogError(InMessage As String, Origin As String = "")

  'Error handler to issue more meaning messages to users.

  'Determine source file
    Dim F as integer
    Dim temp as string
    F = VAL(Mid(Origin, INSTR(Origin, "F") + 1)) 'File

  If UCase(Left(InMessage, Len("RAISECOMPILERERROR:"))) =  "RAISECOMPILERERROR:"  Then
        Origin = Left(Origin, Instr(Origin, "L"))+""+Mid(Origin, Instr(Origin, "S"))
        'Remove CATCH: from string
        InMessage = Mid(InMessage,Len("RAISECOMPILERERROR:")+1)
  End If

  'Handle error messages from incorrect LCD setup
  If InStr(ucase( SourceFile(F).FileName ), "LCD.H") > 0  Then
      If instr(InMessage, "SYSLCDTEMP") > 0  or _
        instr(UCASE(InMessage), "LCD_ENABLE") > 0 or _
        instr(UCASE(InMessage), Ucase(Message("BadParamsSet"))) > 0 or _
        ( instr(UCASE(InMessage), Ucase(Message("NotIONOTVALID"))) > 0 and instr(UCASE(InMessage), Ucase("LCD_EB")) )   _
        Then
          If Instr(Subroutine(GetSubID(Origin))->Name, "BACKLIGHT") > 0 Then
            InMessage = Message( "LCD_Not_Setup" ) + " " + Message("LCD_NO_BACKLITECONSTANT") 
          Else
            InMessage = Message( "LCD_Not_Setup" ) + " Calling " + Subroutine(GetSubID(Origin))->Name + "(). " + InMessage  '"LCD Parameters not setup correctly - please correct the LCD setup. " + Subroutine(GetSubID(Origin))->Name
          End If
          Origin = ""
      End if
  End if


  ErrorsFound = -1
  LogOutputMessage Origin + InMessage + "E"  
  End Sub

Sub LogOutputMessage(InMessage As String)

  Dim As Integer FindDup

  'Check if message is duplicate
  For FindDup = 1 To OutMessages
    If InMessage = OutMessage(FindDup) Then
      'Duplicate found, exit
      Exit Sub
    End If
  Next

  'Error if too many messages
  If OutMessages >= MAX_OUTPUT_MESSAGES - 1 Then
    'If too many warnings, just exit
    If Right(InMessage, 1) = "W" And Not WarningsAsErrors Then Exit Sub

    'If too many errors, error and quit
    If OutMessages = (MAX_OUTPUT_MESSAGES - 1) Then
      OutMessages += 1
      OutMessage(OutMessages) = Message("TooManyErrors") + "E"
    End If

    Return
  End If

  'Log message
  OutMessages += 1
  OutMessage(OutMessages) = InMessage
  
  End Sub

Sub LogWarning(InMessage As String, Origin As String = "")
  LogOutputMessage Origin + InMessage + "W"  
  End Sub

Sub OptimiseCalls

  'Ensure that rcall/call and rjmp/jmp/goto are used appropriately
  'Will measure the distance between the jump and the target. If it's small
  'enough for a relative call, the non-relative jump will be replaced with a
  'relative one to make the program smaller. If there is already a relative
  'jump, but the distance is too great, a non-relative jump will be inserted
  'instead.

  'This routine is AVR and PIC18F compatible only
  If ChipFamily <> 16 And Not ModeAVR Then Exit Sub

  Dim As LinkedListElement Pointer CurrLine
  Dim As ProgLineMeta Pointer CurrMeta
  Dim As HashMap Pointer Labels
  Dim As Integer Pointer LabelPos
  Dim As String JumpTarget, ProperCmd, CheckTarget, NextLine, TempLoc
  Dim As Integer IsRelative, UseRelative, IsJump, LineSize, CurrLinePos
  Dim As Integer JumpSize, IsFirstLine
  Dim As Integer CallChanged, ProgramScans

  ProgramScans = 0
  Do
    'Get list of labels and locations in program
    CallChanged = 0
    ProgramScans += 1
    CurrLine = CompilerOutput->CodeList->Next
    CurrLinePos = 0
    Labels = HashMapCreate
    Do While CurrLine <> 0

      CurrMeta = GetMetaData(CurrLine)

      LineSize = CalcLineSize(CurrLine->Value, 1)
      CurrMeta->LineSize = LineSize
      If LineSize = 0 Then
        If ModeAVR Then
          If Right(CurrLine->Value, 1) = LabelEnd Then
            LabelPos = Callocate(SizeOf(Integer))
            *LabelPos = CurrLinePos
            HashMapSet(Labels, UCase(Left(CurrLine->Value, Len(CurrLine->Value) - 1)), LabelPos)
          End If
        ElseIf ChipFamily = 16 Then
          If CurrLine->Value <> "" And Left(CurrLine->Value, 1) <> ";" And Left(CurrLine->Value, 1) <> " " And Left(CurrLine->Value, 9) <> "PRESERVE " Then
            LabelPos = Callocate(SizeOf(Integer))
            *LabelPos = CurrLinePos
            HashMapSet(Labels, UCase(CurrLine->Value), LabelPos)
          End If
        End If
        If Left(CurrLine->Value, 5) = ".ORG " Then
          CurrLinePos = MakeDec(Trim(Mid(CurrLine->Value, 5)))
        End If
      Else
        CurrLinePos += LineSize
      End If

      CurrLine = CurrLine->Next
    Loop

    'Check program, make changes where needed
    CurrLine = CompilerOutput->CodeList->Next
    CurrLinePos = 0
    Do While CurrLine <> 0

      CurrMeta = GetMetaData(CurrLine)

      'Get current position
      If CurrLinePos = 0 Then
        IsFirstLine = -1
      End If
      If Left(CurrLine->Value, 5) = ".ORG " Then
        CurrLinePos = MakeDec(Trim(Mid(CurrLine->Value, 5)))
      End If
      'Print Hex(CurrLinePos), CurrLine->Value

      'Check to see if command is call or goto
      IsJump = 0
      If ModeAVR Then
        If Left(CurrLine->Value, 7) = " rcall " Or Left(CurrLine->Value, 6) = " call " Then
          IsJump = 1
        ElseIf Left(CurrLine->Value, 6) = " rjmp " Or Left(CurrLine->Value, 5) = " jmp " Then
          IsJump = 2
        End If
      ElseIf ChipFamily = 16 Then
        If Left(CurrLine->Value, 7) = " rcall " Or Left(CurrLine->Value, 6) = " call " Then
          IsJump = 1
        ElseIf Left(CurrLine->Value, 6) = " goto " Or Left(CurrLine->Value, 5) = " bra " Then
          IsJump = 2
        End If
      End If

      If IsJump <> 0 Then
        'Get jump target
        JumpTarget = Trim(CurrLine->Value)

        JumpTarget = Mid(JumpTarget, InStr(JumpTarget, " ") + 1)

        If InStr(JumpTarget, ";") <> 0 Then JumpTarget = Trim(Left(JumpTarget, InStr(JumpTarget, ";") - 1))
        'Check if relative
        IsRelative = 0
        If ModeAVR Then
          If Left(CurrLine->Value, 2) = " r" Then IsRelative = -1
        ElseIf ChipFamily = 16 Then
          If Left(CurrLine->Value, 2) = " r" Or Left(CurrLine->Value, 2) = " b" Then IsRelative = -1
        End If

        'If chip has 4096 or less words program memory, use r* exclusively
        'Otherwise, alter as needed
        If (ModeAVR And ChipProg <= 4096) Or (ChipFamily = 16 And ChipProg < 2048) Then
          UseRelative = -1
        Else
          UseRelative = 0

          'Get label
          CheckTarget = Trim(UCase(JumpTarget))
          'Search 2048 words either way from current location
          'If label found, then can use relative mode
          If CheckTarget = "$" Then
            UseRelative = -1

          ElseIf ModeAVR And WholeInstr(CheckTarget, "PC") = 2 Then
            TempLoc = CheckTarget
            WholeReplace TempLoc, "PC", "0"
            Calculate TempLoc
            If Abs(Val(TempLoc)) < 2048 Then
              'Print "Using relative for "; CheckTarget
              UseRelative = -1
            End If

          'For bootloader compatibility, treat first line as non-relative on all
          'PIC 18F chips.
          ElseIf IsFirstLine And ModePIC Then
            UseRelative = 0

          Else
            LabelPos = HashMapGet(Labels, CheckTarget)
            If LabelPos <> 0 Then

              JumpSize = Abs(*LabelPos - CurrLinePos)

              If (ModeAVR And JumpSize < 2048) Or (ChipFamily = 16 And JumpSize < 1024) Then
                If Instr( Ucase( CurrLine->Value), Ucase(DISABLEOPTIMISATION) ) <> 0 then
                  ' DO NOT OPTIMISE SPECIFIC CODE FOR LGT CHIPS
                  UseRelative = 0
                Else
                  UseRelative = -1
                Endif
              End If
            End If
          End If

        End If

        'If using relative when shouldn't or not when should, change line
        If IsRelative <> UseRelative Then
          CallChanged = -1
          If ModeAVR Then
            If UseRelative Then
              ProperCmd = " r"
            Else
              ProperCmd = " "
            End If
            If IsJump = 1 Then
              ProperCmd += "call "
            Else
              ProperCmd += "jmp "
            End If

          ElseIf ChipFamily = 16 Then
            If UseRelative Then
              If IsJump = 1 Then
                ProperCmd = " rcall "
              Else
                ProperCmd = " bra "
              End If
            Else
              If IsJump = 1 Then
                ProperCmd = " call "
              Else
                ProperCmd = " goto "
              End If
            End If
          End If
          CurrLine->Value = ProperCmd + JumpTarget
          CurrMeta->LineSize = CalcLineSize(CurrLine->Value, 1)
        End If

      End If

      CurrLinePos += CurrMeta->LineSize
      CurrLine = CurrLine->Next
      IsFirstLine = 0
    Loop

    HashMapDestroy(Labels)
  Loop While CallChanged
  'Print "Optimised calls in "; ProgramScans; " attempts"  
  End Sub

Sub OptimiseIF (CompSub As SubType Pointer = 0)
  'Optimise compiled IF statements. Replace this:
  ' conditional skip
  ' goto LABEL
  ' single instruction
  ' LABEL
  'With this:
  ' opposite conditional skip
  ' single instruction
  'This is called at the end of the program, as the final step of compilation.
  '(CompSub = 0, output code optimised)
  'If may also be called before page selection has been added. If this happens,
  'it is possible that single call instructions will become multiple instruction
  'page select, call, page select. If being called before page selection,
  'CompSub will be non-zero, and so calls should not be optimised yet.

  'Should leave labels, in case there is an else if

  Dim As String Temp, InLine, SearchLabel
  Dim As Integer T, EndIfNotUsed
  Dim As LinkedListElement Pointer CurrLine, PL(3), SearchBack

  If CompSub = 0 Then
    CurrLine = CompilerOutput->CodeList->Next
  Else
    CurrLine = CompSub->CodeStart->Next
  End If

  Do While CurrLine <> 0
    Temp = CurrLine->Value

    'Find skip > goto > other > label, replace with opposite skip, other

    IF Left(Temp, 5) = "ENDIF" THEN
      If Right(Temp, 1) = ":" THEN Temp = Left(Temp, LEN(Temp) - 1)

      'Get 3 lines before CurrLine
      PL(0) = CurrLine
      For T = 1 To 3
        If PL(T - 1) <> 0 Then PL(T) = PL(T - 1)->Prev
        'Remove comments from list
        Do While Left(PL(T)->Value, 9) = "PRESERVE "
          If PL(T)->Prev <> 0 Then
            PL(T) = PL(T)->Prev
          Else
            Exit Do
          End If
        Loop
      Next

      'Before:    After:
      '3 btfsc    btfss
      '2 goto   inst
      '1 inst
      '0 label < Looking at this

      'If compiling a subroutine, check if instruction is call
      If CompSub <> 0 Then
        If Left(PL(1)->Value, 6) = " call " Or Left(PL(1)->Value, 7) = " rcall " Then
          'Call found, need to skip
          GoTo OptimiseNextIf
        EndIf
      End If

      If ModePIC Then
        IF PL(2)->Value = " goto " + Temp Or PL(2)->Value = " bra " + Temp Then
          InLine = Left(PL(3)->Value, 6)
          IF InLine = " btfsc" THEN T = 0
          IF InLine = " btfss" THEN T = 1
          IF T = 0 THEN Replace PL(3)->Value, "btfsc", "btfss"
          IF T = 1 THEN Replace PL(3)->Value, "btfss", "btfsc"
          LinkedListDelete(PL(2))
          If Left(PL(1)->Value, 4) <> "ELSE" Then
            'CurrLine = LinkedListDelete(CurrLine)
            'Check that ENDIF isn't used
            EndIfNotUsed = -1
            SearchLabel = " " + CurrLine->Value
            SearchBack = CurrLine->Prev
            Do While SearchBack <> 0
              If Right(SearchBack->Value, Len(SearchLabel)) = SearchLabel Then
                EndIfNotUsed = 0
                Exit Do
              End If
              SearchBack = SearchBack->Prev
            Loop
            If EndIfNotUsed Then
              CurrLine = LinkedListDelete(CurrLine)
            End If
          End If
        END If

      ElseIf ModeAVR Then
        IF PL(2)->Value = " rjmp " + Temp Or PL(2)->Value = " jmp " + Temp THEN
          InLine = Left(PL(3)->Value, 5)
          T = -1
          IF InLine = " sbrc" THEN T = 0
          IF InLine = " sbrs" THEN T = 1
          IF InLine = " sbic" THEN T = 2
          IF InLine = " sbis" THEN T = 3
          IF InLine = " brbc" THEN T = 4
          IF InLine = " brbs" THEN T = 5
          IF T = 0 THEN Replace PL(3)->Value, "sbrc", "sbrs"
          IF T = 1 THEN Replace PL(3)->Value, "sbrs", "sbrc"
          IF T = 2 THEN Replace PL(3)->Value, "sbic", "sbis"
          IF T = 3 THEN Replace PL(3)->Value, "sbis", "sbic"
          IF T = 4 THEN Replace PL(3)->Value, "brbc", "brbs"
          IF T = 5 THEN Replace PL(3)->Value, "brbs", "brbc"
          If T <> -1 Then
            LinkedListDelete(PL(2))
            'CurrLine = LinkedListDelete(CurrLine)
            If Left(PL(1)->Value, 4) <> "ELSE" Then
              'CurrLine = LinkedListDelete(CurrLine)
              'Check that ENDIF isn't used
              EndIfNotUsed = -1
              SearchLabel = " " + Left(CurrLine->Value, Len(CurrLine->Value) - 1)
              SearchBack = CurrLine->Prev
              Do While SearchBack <> 0
                If Right(SearchBack->Value, Len(SearchLabel)) = SearchLabel Then
                  EndIfNotUsed = 0
                  Exit Do
                End If
                SearchBack = SearchBack->Prev
              Loop
              If EndIfNotUsed Then
                CurrLine = LinkedListDelete(CurrLine)
              End If

            End If
          End If
        END IF
      End If

      OptimiseNextIf:
    END If

    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub OptimiseIncrement(CompSub As SubType Pointer)
  'PIC only
  'Find uses of incf followed by btfss STATUS, Z, replace with single incfsz
  'Mainly for output of CompileCalcAddition, which would use an incfsz but must allow for bank selection
  If Not ModePIC Then Exit Sub

  Dim As LinkedListElement Pointer CurrLine

  CurrLine = CompSub->CodeStart->Next

  Do While CurrLine <> 0

    If Left(CurrLine->Value, 6) = " incf " Then
      If CurrLine->Next <> 0 Then
        If CurrLine->Next->Value = " btfss STATUS,Z" Then
          CurrLine->Value = " incfsz " + Mid(CurrLine->Value, 7)
          LinkedListDelete(CurrLine->Next)
        End If
      EndIf
    End If

    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Sub PreparePageData
  'Generate prog mem page data
  Dim As Integer PageSize, FirstPageStart, FindLastVector, LastVector, LastLoc, IsFirstPage
  Dim As Integer AvailableChipProg

  PageSize = 0
  AvailableChipProg = ChipProg - ReserveHighProg

  If ModePIC Then
    'On low end, have 512 word pages
    If ChipFamily = 12 Then
      PageSize = 512
      If HasSFR("INTCON") Then
        FirstPageStart = 5
      Else
        FirstPageStart = 0
      End If

    'Midrange, 2048 words
    'First page starts at location 5, after interrupt
    ElseIf ChipFamily = 14 Or ChipFamily = 15 Then
      PageSize = 2048
      If HasSFR("INTCON") Then
        FirstPageStart = 5
      Else
        FirstPageStart = 0
      End If

    '18F, single page
    'First page starts at location 12, after interrupt
    ElseIf ChipFamily = 16 Then
      FirstPageStart = 12

    End If
  ElseIf ModeAVR Then
    'No pages
    PageSize = 0
    'Find last interrupt vector
    LastVector = 0
    For FindLastVector = 1 To IntCount
      With Interrupts(FindLastVector)
        If .VectorLoc > LastVector Then LastVector = .VectorLoc
      End With
    Next
    FirstPageStart = LastVector + 2

  ElseIf ModeZ8 Then
    'Single page?
    PageSize = 0
    LastVector = 0
    For FindLastVector = 1 To IntCount
      With Interrupts(FindLastVector)
        If .VectorLoc > LastVector Then LastVector = .VectorLoc
      End With
    Next
    FirstPageStart = LastVector + 1
  End If
  'Generate page info
  ProgMemPage(0).PageSize = PageSize
  LastLoc = 0
  Do While LastLoc < AvailableChipProg
    ProgMemPages += 1
    With ProgMemPage(ProgMemPages)
      'Set first location in page
      If ProgMemPages = 1 Then
        .StartLoc = FirstPageStart + Bootloader
      Else
        .StartLoc = LastLoc
      End If

      'Calc and set last location
      If PageSize = 0 Then
        LastLoc = AvailableChipProg
      Else
        LastLoc += PageSize
        If LastLoc > AvailableChipProg Then LastLoc = AvailableChipProg
      End If
      .EndLoc = LastLoc - 1
      .CodeSize = 0
      .MaxSize = .EndLoc - .StartLoc + 1
      .PageSize = PageSize
    End With
  Loop
  
  End Sub

Sub PrepareProgrammer
  'Prepare programmer tool
  'Need to select appropriate programmer for current chip model
  'If programmer specified but assembler isn't, use gcasm
  Dim As LinkedListElement Pointer ProgrammerList, CurrProg
  Dim As ExternalTool Pointer CurrTool
  Dim As String Cmd, OldCmd, Temp
  Dim As Integer RecDetect

  'Trim quotes from exe names
  AsmExe = Trim(AsmExe, Chr(34))
  PrgExe = Trim(PrgExe, Chr(34))

  'If there is a list of programmers, choose the first appropriate one
  PrgTool = 0
  If InStr(PrgExe, ",") <> 0 Then
    If VBS = 1 Then
      Temp = Message("ChoosingProgrammer")
      Print Spc(5); Temp
    End If
    ProgrammerList = GetElements(PrgExe, ",")
    CurrProg = ProgrammerList->Next
    Do While CurrProg <> 0
      CurrTool = GetTool(CurrProg->Value)
      If CurrTool <> 0 Then
        With *CurrTool
          If .UseIf = "" Then
            'Found programmer with no conditions, use
            If VBS = 1 Then
              Temp = Message("ProgrammerSelected")
              Replace Temp, "%prog%", .DispName
              Print Spc(10); Temp
            End If
            PrgTool = CurrTool
            Exit Do
          Else
            'Found programmer with conditions, check
            Cmd = Ucase(.UseIf)

            '            If VBS = 1 Then
            '                Print Spc(10);
            '                Print "USEIF " + Cmd;
            '            End If
            OldCmd = ""
            RecDetect = 0
            Do While OldCmd <> Cmd
              OldCmd = Cmd
              Cmd = ReplaceConstantsLine(CheckSysVarDef(Cmd), 0)
              RecDetect += 1
              'If have looped too many times, there is probably a recursive define
              If RecDetect > 100 Then Exit Do
            Loop
            'Change <> to ~
            If Instr( Cmd, "<>" ) > 0 then Replace Cmd, "<>","~"
                '            If VBS = 1 Then
                '                Print Spc(4);
                '                Print " equates to '" + Cmd+"' = ";
                '            End If
                            Calculate Cmd
                            If Val(Cmd) <> 0 Then
                              'Condition is true, use programmer
                              'Found programmer with no conditions, use
                              If VBS = 1 Then
                '                Print " = -" + Trim(Cmd)
                Print Spc(10);
                Temp = Message("ProgrammerSelected")
                Replace Temp, "%prog%", .DispName
                Print Temp
              End If

              PrgTool = CurrTool
              Exit Do
            Else

              'Found programmer with no conditions, use
              If VBS = 1 Then
                Print Spc(10);
                Temp = Message("ProgrammerSkipped")
                Replace Temp, "%prog%", .DispName
                Print Temp
              End If
            End If

          End If
        End With
      End If

      CurrProg = CurrProg->Next
    Loop

    'If PrgTool is still zero, no programmers were valid
    If PrgTool = 0 Then
      PrgExe = "none"
      LogWarning(Message("WarningNoValidProgrammer"))
    End If

  End If

  'If tool specified for assembler or programmer, use it
  AsmTool = GetTool(AsmExe)
  If AsmTool <> 0 Then
    With *AsmTool
      AsmExe = ReplaceToolVariables(.Cmd, , , AsmTool)

      AsmParams = .Params
    End With
  End If


  If PrgTool = 0 Then PrgTool = GetTool(PrgExe)
  If PrgTool <> 0 Then
    With *PrgTool
      PrgName = .DispName
      PrgExe = ReplaceToolVariables(.Cmd, , , PrgTool)
      PrgParams = .Params
      PrgDir = .WorkingDir
    End With
  End If

  'If we have a programmer but not an assembler, use gcasm
  If AsmExe = "" And PrgExe <> "" Then
    AsmExe = "gcasm"
  End If

  'Add full path to assembler and programmer names
  #IFDEF __FB_UNIX__
    If AsmExe <> "" And Left(AsmExe, 1) = "." Then AsmExe = ID + Mid(AsmExe, 2)
    If PrgExe <> "" And Left(PrgExe, 1) = "." Then PrgExe = ID + Mid(PrgExe, 2)
  #ELSE

    If AsmExe <> "" And Mid(AsmExe, 2, 1) <> ":" And UCASE(AsmExe) <> "GCASM" Then
      If instr(  UCASE(AsmExe), "PIC-AS" ) = 0 then
        If Left(AsmExe, 1) = "\" Then AsmExe = Mid(AsmExe, 2)
        AsmExe = ID + "\" + AsmExe
      End if
    End If

    If PrgExe <> "" And Mid(PrgExe, 2, 1) <> ":" Then
      If Left(PrgExe, 1) = "\" Then PrgExe = Mid(PrgExe, 2)
      PrgExe = ID + "\" + PrgExe
    End If
  #EndIf
  
  End Sub

Sub ProcessArrays (CompSub As SubType Pointer)
  Dim As String InLine, Origin, Temp, AV, ArrayName, ArrayType, ArrayPosition
  Dim As String ArrayHandler, AppendArrayPosition, AliasLoc, OldLine
  Dim As Integer ATV, ArraysInLine, CD, SS, UseTempVar, ArrayElementSize, CurrByte
  Dim As Integer ArrayDir, L, P, ArrayPointer, NCO, MarkBlock
  Dim As LinkedListElement Pointer CurrLine, NewCodeList, NewCodeLine, LastArray, ArrayFound

  Dim As SubType Pointer MainSub
  MainSub = Subroutine(0)

  'Combine local arrays and global arrays into single list
  Dim As LinkedListElement Pointer Variables, CurrVarLoc
  Dim As VariableType Pointer CurrVar
  Variables = HashMapToList(@(MainSub->Variables))
  LinkedListInsertList(Variables, HashMapToList(@(CompSub->Variables)))
  CurrVarLoc = Variables->Next
  Do While CurrVarLoc <> 0
    'Remove any single element variables
    CurrVar = CurrVarLoc->MetaData
    If CurrVar->Size < 2 Then
      CurrVarLoc = LinkedListDelete(CurrVarLoc, 0)
    End If
    CurrVarLoc = CurrVarLoc->Next
  Loop

  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    InLine = CurrLine->Value
    'Color 1
    'Print ,, InLine
    'Color 7

    'No brackets means no arrays in line
    If Instr(InLine, "(") = 0 Then Goto CompileArraysNextLine

    'Some things will look like arrays but are not
    If (WholeINSTR(InLine, "low") = 2 And InStr(InLine, "low(") <> 0) Or (WholeINSTR(InLine, "high") = 2 And InStr(InLine, "high(") <> 0) Then Goto CompileArraysNextLine

    Origin = ""
    IF INSTR(InLine, ";?F") <> 0 THEN
      Origin = Mid(InLine, INSTR(InLine, ";?F"))
      InLine = RTrim(Left(InLine, INSTR(InLine, ";?F") - 1))
    END IF
    ATV = 0 'Number of array temporary variables

    'Mark array operation as block?
    MarkBlock = 0
    If Left(InLine, 3) = "DO " Or Left(InLine, 5) = "WAIT " Or Left(InLine, 5) = "LOOP " Then MarkBlock = -1

    'Count the number of arrays in the line
    ArraysInLine = 0
    Temp = InLine
    'Local arrays
    CurrVarLoc = Variables->Next
    Do While CurrVarLoc <> 0
      CurrVar = CurrVarLoc->MetaData
      With *CurrVar
        If .Size > 1 Then
          Do While WholeINSTR(Temp, .Name) = 2 AND INSTR(Temp, "(") <> 0
            ArraysInLine += 1
            WholeReplace Temp, "[1]" + .Name, ""
            'Print .Name, ArraysInLine, Temp
          Loop
        END IF
      End With

      CurrVarLoc = CurrVarLoc->Next
    Loop

    'Does the line contain an array?
    LastArray = Variables->Next
  CheckArrayAgain:
    ArrayFound = 0
    CurrVarLoc = LastArray
    Do While CurrVarLoc <> 0
      CurrVar = CurrVarLoc->MetaData
      With *CurrVar
        If .Size > 1 Then
          'WholeINSTRdebug=-1
          'print origin; "{"+str(WholeINSTR(InLine, .Name ))+"} "
          IF ( WholeINSTR(InLine, .Name ) = 2 AND INSTR(InLine, "(") <> 0)  THEN
            IF WholeINSTR(InLine, .Name + "()") = 2 THEN
              ArrayFound = 0
             
              IF Instr( Inline, .Name ) <> 1 Then
                'This permits the support of  array population such as array() = 1,2,3,4....
                Temp = Message("ArrayAssignmentIncorrect")
                Replace Temp, "%Name%", .Name
                LogError Temp, Origin
              END IF
              
            Else
              ArrayFound = CurrVarLoc
              EXIT Do
            End If
          END IF
          'WholeINSTRdebug=0          
        END IF
      End With

      CurrVarLoc = CurrVarLoc->Next
    Loop

    'Show error if array has not been found but brackets have
    IF ArrayFound = 0 AND INSTR(InLine, "(") <> 0 AND INSTR(InLine, "()") = 0 AND INSTR(InLine, "$") = 0 AND Left(InLine, 6) <> " retlw" THEN
      InLine = Left(InLine, INSTR(InLine, "(") - 1)
      'Get name of just array/function
      FOR SS = LEN(InLine) TO 1 STEP -1
        IF Mid(InLine, SS, 1) = " " THEN InLine = Mid(InLine, SS + 1): Exit For
      NEXT
      'High and Low are built in assembler Function on AVR, ignore them
      If ModeAVR And (UCase(Trim(InLine)) = "LOW" OR UCase(Trim(InLine)) = "HIGH") Then Goto CompileArraysNextLine
      'Casting operations
      If Left(InLine, 1) = "[" And Right(InLine, 1) = "]" Then Goto CompileArraysNextLine

      'Check if brackets follow a calculation symbol, show error if they don't
      IF InLine <> "" THEN
        IF Not IsCalc(RIGHT(InLine, 1)) And InLine <> "IF" Then
          Temp = Message("ArrayNoDec")
          Replace Temp, "%Name%", InLine
          LogError Temp, Origin
        End IF
      END IF
    END IF

    'Array has been found, so generate code to access it
    IF ArrayFound <> 0 THEN

      'Print "Array found in line " + CurrLine->Value

      'Get array type
      CurrVar = ArrayFound->MetaData
      With *CurrVar
        ArrayName = .Name
        ArrayType = .Type
      End With
      If ArrayType = "STRING" Then ArrayType = "BYTE"
      ArrayElementSize = GetTypeSize(ArrayType)
      UseTempVar = -1
      If ArraysInLine = 1 And ArrayElementSize = 1 Then UseTempVar = 0

      NewCodeList = LinkedListCreate
      NewCodeLine = NewCodeList

      If MarkBlock Then
        NewCodeLine = LinkedListInsert(NewCodeLine, ";BLOCKSTART," + ArrayName)
      End If

      'Mark array as used
      CurrVar->Used = -1

      'Is array being read or set?
      ArrayDir = 0 '0 = read, 1 = set
      If UseTempVar Then
        'These commands set variables:
        IF Left(InLine, 4) = "SET " THEN ArrayDir = 1
        'IF INSTR(InLine, "=") > INSTR(InLine, ArrayName) THEN ArrayDir = 1
        IF INSTR(InLine, "=") <> 0 Then
          'If array name before =, is being set
          Temp = Left(InLine, InStr(InLine, "=") - 1)
          If WholeINSTR(Temp, ArrayName) = 2 Then ArrayDir = 1
        End If
        If LCase(Left(LTrim(InLine), 6)) = "movwf " THEN ArrayDir = 1
        'These commands read variables, but may appear to set them based on above
        IF Left(InLine, 3) = "IF " THEN ArrayDir = 0
        IF Left(InLine, 3) = "DO " THEN ArrayDir = 0
        IF Left(InLine, 5) = "WAIT " THEN ArrayDir = 0
        IF Left(InLine, 10) = "READTABLE " Then
          Temp = InLine
          Replace Temp, ",", ""
          IF INSTR(Temp, ",") < INSTR(Temp, ArrayName) THEN ArrayDir = 1
        END IF
      Else
        ArrayDir = 0
      End If

      'Get the array name and index
      Temp = Mid(InLine, WholeInstrLoc(InLine, ArrayName))

      L = 1
      P = INSTR(Temp, "(")
      Do
        P += 1
        IF Mid(Temp, P, 1) = "(" THEN L = L + 1
        IF Mid(Temp, P, 1) = ")" THEN L = L - 1
      Loop While L <> 0 AND P < LEN(Temp)
      IF L <> 0 THEN
        LogError Message("BadBrackets"), Origin
        GoTo CompileArraysNextLine
      Else
        Temp = Left(Temp, P)

        'Get array index
        ArrayPosition = Mid(Temp, INSTR(Temp, "(") + 1)
        ArrayPosition = Left(ArrayPosition, Len(ArrayPosition) - 1)
        'Print "Accessing "; ArrayName; " index "; ArrayPosition

        'Get array type (real/pointer)
        IF CurrVar->Pointer = "REAL" THEN
          ArrayHandler = "@" + ArrayName
          ArrayPointer = 0
        END IF
        IF CurrVar->Pointer = "POINTER" THEN
          ArrayHandler = "Sys" + ArrayName + "Handler"
          ArrayPointer = -1
        END If

        'Access location directly?
        If IsConst(ArrayPosition) And Not ArrayPointer Then
          'If using a fixed element of a real array, treat it as a single element variable
          'Create alias to refer to the exact memory location directly and use that
          AV = "SYS" + ArrayName + "_" + Str(MakeDec(ArrayPosition))
          AliasLoc = ""
          For CurrByte = ArrayElementSize - 1 To 0 Step -1
            If AliasLoc = "" Then
              AliasLoc = ArrayName + " + " + Str(MakeDec(ArrayPosition) * ArrayElementSize + CurrByte)
            Else
              AliasLoc += ", " + ArrayName + " + " + Str(MakeDec(ArrayPosition) * ArrayElementSize + CurrByte)
            End If
          Next
          AddVar (AV, ArrayType, 1, 0, "ALIAS:" + AliasLoc, Origin, , -1)
          'Can't put array name back into line, causes problem if array accessed twice
          WholeReplace InLine, "[1]" + Temp, TempRemove(AV)
          CurrLine->Value = InLine + Origin

        'Need to use indirect addressing for array
        Else

          'Alter line
          If UseTempVar Then
            ATV = ATV + 1
            AV = "SysArrayTemp" + Str(ATV)
            AddVar AV, ArrayType, 1, CompSub, "REAL", Origin, , -1
          Else
            If ChipFamily = 12 Or ChipFamily = 14 Then AV = "INDF"
            If ChipFamily = 15 Or ChipFamily = 16 Then AV = "INDF0"
            If ModeAVR Then AV = "SysPointerX"
          End If
          OldLine = InLine
          WholeReplace InLine, "[1]" + Temp, AV
          If OldLine = InLine Then
            'Replace failed, line is invalid
            Temp = Message("SynErr")
            LogError Temp, Origin
            CurrLine = LinkedListDelete(CurrLine)
            GoTo CompileArraysNextLine
          End If
          CurrLine->Value = InLine + Origin
          If ArrayPointer Then AddVar ArrayHandler, "WORD", 1, 0, "REAL", Origin, , -1

          'Get position to set
          If IsConst(ArrayPosition) And MakeDec(ArrayPosition) = 0 Then
            AppendArrayPosition = ""
          Else
            If IsCalc(ArrayPosition) Then
              AppendArrayPosition = "+(" + ArrayPosition + ")*" + Str(ArrayElementSize)
            Else
              AppendArrayPosition = "+" + ArrayPosition + "*" + Str(ArrayElementSize)
            End If
          End If

          'Add code to read/set array
          If ModePIC And (ChipFamily = 12 Or ChipFamily = 14) Then
            If ArrayPointer = 0 Then
              NewCodeLine = LinkedListInsert(NewCodeLine, "FSR = [byte]" + ArrayHandler + AppendArrayPosition + Origin)
              If HighFSR Then
                NewCodeLine = LinkedListInsert(NewCodeLine, " bankisel " + ArrayName)
              End If
            Else
              NewCodeLine = LinkedListInsert(NewCodeLine, "FSR = " + ArrayHandler + AppendArrayPosition + Origin)
              If HighFSR Then
                NewCodeLine = LinkedListInsert(NewCodeLine, " bcf STATUS, IRP")
                NewCodeLine = LinkedListInsert(NewCodeLine, " btfsc " + ArrayHandler + "_H,0")
                NewCodeLine = LinkedListInsert(NewCodeLine, " bsf STATUS, IRP")
              End If
            End If
            If UseTempVar Then
              For CurrByte = 0 To ArrayElementSize - 1
                If ArrayDir = 0 THEN NewCodeLine = LinkedListInsert(NewCodeLine, GetByte(AV, CurrByte) + " = INDF")
                IF ArrayDir = 1 THEN NewCodeLine = LinkedListInsert(NewCodeLine, "INDF = " + GetByte(AV, CurrByte))
                If CurrByte < ArrayElementSize - 1 Then
                  NewCodeLine = LinkedListInsert(NewCodeLine, " incf FSR,F")
                End If
              Next
            End If

          ElseIf ModePIC And ChipFamily = 15 Then
            NewCodeLine = LinkedListInsert(NewCodeLine, "AFSR0 = " + ArrayHandler + AppendArrayPosition + Origin)
            If UseTempVar Then
              For CurrByte = 0 To ArrayElementSize - 1
                If ArrayDir = 0 THEN NewCodeLine = LinkedListInsert(NewCodeLine, GetByte(AV, CurrByte) + " = INDF0")
                If ArrayDir = 1 THEN NewCodeLine = LinkedListInsert(NewCodeLine, "INDF0 = " + GetByte(AV, CurrByte))
                If CurrByte < ArrayElementSize - 1 Then
                  NewCodeLine = LinkedListInsert(NewCodeLine, " addfsr 0,1")
                End If
              Next
            End If

          ElseIf ModePIC And ChipFamily = 16 Then
            If ArrayPointer = 0 Then
              If ArrayElementSize > 1 Then
                If IsCalc(ArrayPosition) Then
                  ArrayPosition = "(" + ArrayPosition + ")*" + Str(ArrayElementSize)
                Else
                  ArrayPosition += "*" + Str(ArrayElementSize)
                End If
              End If
              If IsConst(ArrayPosition) Then
                NewCodeLine = LinkedListInsert(NewCodeLine, " lfsr 0," + ArrayName + "+" + ArrayPosition)
              Else
                NewCodeLine = LinkedListInsert(NewCodeLine, " lfsr 0," + ArrayName)
                NewCodeLine = LinkedListInsert(NewCodeLine, "AFSR0 = AFSR0 +" + ArrayPosition + Origin)
              End If
            Else
              NewCodeLine = LinkedListInsert(NewCodeLine, "AFSR0 = " + ArrayHandler + AppendArrayPosition + Origin)
            End If
            If UseTempVar Then
              For CurrByte = 0 To ArrayElementSize - 1
                If ArrayDir = 0 THEN NewCodeLine = LinkedListInsert(NewCodeLine, GetByte(AV, CurrByte) + " = POSTINC0")
                If ArrayDir = 1 THEN NewCodeLine = LinkedListInsert(NewCodeLine, "POSTINC0 = " + GetByte(AV, CurrByte))
              Next
            End If

          ElseIf ModeAVR Then
            AddVar "SysStringA", "WORD", 1, 0, "REAL", Origin, , -1
            If ArrayPointer = 0 Then
              NewCodeLine = LinkedListInsert(NewCodeLine, "SysStringA = @" + ArrayName + AppendArrayPosition + Origin)
            Else
              NewCodeLine = LinkedListInsert(NewCodeLine, "SysStringA = " + ArrayHandler + AppendArrayPosition + Origin)
            End If
            If UseTempVar Then
              For CurrByte = 0 To ArrayElementSize - 1
                If ArrayDir = 0 THEN NewCodeLine = LinkedListInsert(NewCodeLine, GetByte(AV, CurrByte) + " = SysPointerXInc")
                IF ArrayDir = 1 THEN NewCodeLine = LinkedListInsert(NewCodeLine, "SysPointerXInc = " + GetByte(AV, CurrByte))
              Next
            End If
          End If

        END IF

        If MarkBlock Then
          NewCodeLine = LinkedListInsert(NewCodeLine, ";BLOCKEND," + ArrayName)
        End If

        'Add array access code
        'Add after line when ArrayDir = 1, before when = 0
        If ArrayDir = 0 Then
          LinkedListInsertList(CurrLine->Prev, NewCodeList)
        Else
          LinkedListInsertList(CurrLine, NewCodeList)
        End If
      End If

      'Check the line again to see if it contains another array
      LastArray = ArrayFound
      GoTo CheckArrayAgain
    END IF

  CompileArraysNextLine:
    CurrLine = CurrLine->Next
  Loop

  'Deal with any missed names
  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    'Replace tokens with names
    CurrLine->Value = ReplaceFnNames(CurrLine->Value)
    CurrLine = CurrLine->Next
  Loop
  
  End Sub

Function PutInRegister(ByRef OutList As LinkedListElement Pointer, SourceValue As String, RegType As String, Origin As String) As String
  'Take any value, and put it into a register
  'Return name of register
  Dim As String Temp, OutVar, CurrRegType
  Dim As Integer Overwrite, ForceHighReg, UpgradeType

  'Try to create new list
  If OutList = 0 Then OutList = LinkedListCreate

  Overwrite = 0
  ForceHighReg = 0
  Do While Mid(SourceValue, 2, 1) = ":"
    'Check if register will be overwritten
    If Left(SourceValue, 2) = "O:" Then
      SourceValue = Mid(SourceValue, 3)
      Overwrite = -1
    End If
    'Check if register needs to be high
    If Left(SourceValue, 2) = "H:" Then
      SourceValue = Mid(SourceValue, 3)
      ForceHighReg = -1
    End If
  Loop

  'AVR Only
  If Not ModeAVR Then
    Return SourceValue
  End If

  'If no type, decide one
  CurrRegType = TypeOfValue(SourceValue, Subroutine(GetSubID(Origin)))
  If RegType = "" Then RegType = CurrRegType

  'Might need to upgrade type
  UpgradeType = 0
  If CastOrder(RegType) > CastOrder(CurrRegType) Then UpgradeType = -1

  'If already a register, then return name
  If IsRegister(SourceValue) And Not Overwrite And Not ForceHighReg Then
    'Might need to upgrade type of register
    If UpgradeType Then
      IF Left(UCase(SourceValue), 7) = "SYSTEMP" Then
        'Need to upgrade neatly
        If CurrRegType = "BYTE" And RegType = "WORD" Then
          LinkedListInsert(OutList, " clr " + GetByte(SourceValue, 1))
        End If
        UpgradeCalcVar SourceValue, RegType
        Return SourceValue
      End If
    Else
      Return SourceValue
    End If
  End If

  'Create temp var
  Temp = ""
  If IsConst(SourceValue) Or ForceHighReg Then Temp = "H:"
  OutVar = GetCalcVar(Temp + RegType)

  'Copy value
  'OutList = CompileVarSet(SourceValue, OutVar, Origin)
  LinkedListInsertList(OutList, CompileVarSet(SourceValue, OutVar, Origin))
  Return OutVar
  
  End Function

Sub ReadPICASChipData
  'Get filename
  #IFDEF __FB_UNIX__
    'not supported
  #ELSE

    'read the INC in and put into ARRAY   ReverseIncFileLookup(
    dim as string InLine, tmpfilename
    dim as integer ffile, fresult,findex
    dim as string SearchExe
    dim as integer F, Result, ChipSupported

    'ChipPICASConfigFile,ChipPICASRoot

    ChipPICASRoot = Left( AsmExe, InStrRev( ReplaceToolVariables(AsmExe,,, AsmTool), "\"))
    ChipPICASRoot = Left( ChipPICASRoot , InStrRev( ChipPICASRoot , "\")-1)
    ChipPICASRoot = Left( ChipPICASRoot , InStrRev( ChipPICASRoot , "\")-1)
    ChipPICASDataFile = Left( ChipPICASRoot , InStrRev( ChipPICASRoot , "\"))+"pic\include\proc\pic"+LCase(ChipName) + ".inc"
    ChipPICASDataFile = mid(ChipPICASDataFile, 2)

    ChipPICASConfigFile = Left( ChipPICASRoot , InStrRev( ChipPICASRoot , "\"))+"pic\dat\cfgmap\"+LCase(ChipName) + ".cfgmap"
    ChipPICASConfigFile = mid( ChipPICASConfigFile , 2)

    ChipPICASConfigFileV3 = Left( ChipPICASRoot , InStrRev( ChipPICASRoot , "\"))+"pic\dat\cfgdata\"+LCase(ChipName) + ".cfgdata"
    ChipPICASConfigFileV3 = mid( ChipPICASConfigFileV3 , 2)

    SearchExe =  AsmExe
    replace SearchExe, "\\","\"

    ReplaceAll ( SearchExe, Chr(34), "" )
    If right(SearchExe, 1) = "\" Then SearchExe = Left(SearchExe, len(SearchExe)-1)
    If trim(dir(SearchExe)) = "" Then
      LogError Message("PICASAssemblerNotFound")
      LogError ChipPICASRoot+"\bin\pic-as.exe " + Message( "PICASNotFound" )      
      WriteErrorLog
      ErrorsFound = - 1
      End
    End If

    'Now check if chip is supported
    tmpfilename = FI
    replace ( tmpfilename, ".GCB", ".tmp")
    kill tmpfilename
    F = FreeFile
    
    'Execute the PIC-AS compiler to provide a list of the chips
    Result = Shell( chr(34)+chr(34)+SearchExe+chr(34)+" -mprint-devices > " + chr(34) + tmpfilename + chr(34) + chr(34) )
    Open tmpfilename For Input As #F
    ChipSupported = 0

    DO WHILE NOT EOF(F)
      LINE INPUT #F, InLine
      If Instr( Ucase(Inline), Ucase(ChipName) ) > 0 Then      
          ChipSupported = -1
      End if
    Loop
    Close #F
    kill tmpfilename
    if ChipSupported = 0 Then
      LogError Message("PICASChipNotSupported")
      WriteErrorLog
      ErrorsFound = - 1
      End
    End if

  
    'now check that the chip data file is present
      findex = 0
      ' Find the first free file number.
      ffile = FreeFile
      if trim(dir(ChipPICASDataFile)) = "" Then
          'Try 3.xx location
          Replace ChipPICASDataFile ,"-AS\PIC", ""
          if trim(dir(ChipPICASDataFile)) = "" Then
            LogError Message("PICASChipNotSupported")
            WriteErrorLog
            ErrorsFound = - 1
            end
          End If
      End if

      fresult = OPEN ( ChipPICASDataFile For Input As ffile )
      If fresult <> 0 then
          LogError Message("PICASNotFound")
          WriteErrorLog
          ErrorsFound = - 1
          end
      End if

      DO WHILE NOT EOF(ffile)
        LINE INPUT #ffile, InLine
        if left( InLine,7) =  "#define" and instr( inline, "BANKMASK") = 0  <> 0 then
          'read next line
          LINE INPUT #ffile, InLine

          If instr( ucase(inline), "EQU") > 0 then
            ReverseIncFileLookup( findex ).Value = trim(mid(inline, 1, instr(inline," equ ")-5))
            ReverseIncFileLookup( findex ).NumVal = val("&h"+trim( mid( inline,instr(inline," equ ")+5,  InStr( inline,chr(34) ) -1 )  ))
            findex = findex +1
          end if
        end if

      LOOP
      Close #ffile

    'print ChipPICASConfigFile
    'print ChipPICASConfigFileV3
    'Check that the cfg data is present
      findex = 0
      ' Find the first free file number.
      ffile = FreeFile
      fresult = OPEN ( ChipPICASConfigFile For Input As ffile )
      Select Case fresult
        Case 0:
          DO WHILE NOT EOF(ffile)
            LINE INPUT #ffile, InLine
            if instr( inline, ":") <> 0 and instr( inline, "=") <> 0 then
              'read next line
              'PLLSEL_PLL3X:PLLSEL=PLL3X
              ReverseCfgFileLookup( findex ).Value = trim(mid(inline, instr(inline,":")+1, instr(inline,"=")-instr(inline,":")-1 ))
              ReverseCfgFileLookup( findex ).State = trim(mid(inline, instr(inline,"=")+1))
              ReverseCfgFileLookup( findex ).Config = ReverseCfgFileLookup( findex ).Value+"="+ReverseCfgFileLookup( findex ).State
                  'print ReverseCfgFileLookup( findex ).Value
                  'print ReverseCfgFileLookup( findex ).State
                  'print ReverseCfgFileLookup( findex ).Config
              findex = findex +1

            end if

          LOOP
          Close #ffile
        Case 2: 
          Dim CSetting as String = ""
          ' fall back option inrtoduce at PIC-AS v3.00
          if trim(dir(ChipPICASConfigFileV3)) = "" Then replace ChipPICASConfigFileV3,"-AS\PIC", ""
          fresult = OPEN ( ChipPICASConfigFileV3 For Input As ffile )
          Select Case fresult
            Case 0
              DO WHILE NOT EOF(ffile)
                LINE INPUT #ffile, InLine
                Dim as String cfgdata()
                
                if (left(inline,1) <> "#") and ( instr( inline, ":") <> 0 ) then

                  '# for each CWORD the configuration settings are listed as
                  '# 
                  '#     CSetting:<mask>:<name>[,<alias list>]:<description>
                  '# 
                  '# lastly for each CSetting all possible values are listed as
                  '# 
                  '#     CVALUE:<value>:<name>[,<alias list>]:<description>
                  'CSetting:3:FOSC:Oscillator Selection bits
                  'CVALUE:3:EXTRC,_RC_OSC:RC oscillator
                  'CVALUE:2:HS,_HS_OSC:HS oscillator
                  'CVALUE:1:XT,_XT_OSC:XT oscillator
                  'CVALUE:0:LP,_LP_OSC:LP oscillator

                  StringSplit ( InLine, ":",-1,cfgdata() )
                  
                  If Ucase( Trim(cfgdata(0))) = "CSETTING" Then
                    ReverseCfgFileLookup( findex ).Value = Trim(cfgdata(2))
                    CSetting = Trim(cfgdata(1))         
                  End If
                  
                  If Ucase( Trim(cfgdata(0))) = "CVALUE" and CSetting  <> "" Then
                    If Ucase( Trim(cfgdata(1))) = CSetting Then
                      If Instr(cfgdata(2), ",") > 0 Then
                        ReverseCfgFileLookup( findex ).State = Left(Trim(cfgdata(2)),Instr(cfgdata(2), ",")-1)
                      Else
                        ReverseCfgFileLookup( findex ).State = Trim(cfgdata(2))
                      End If
                      ReverseCfgFileLookup( findex ).Config = ReverseCfgFileLookup( findex ).Value+"="+ReverseCfgFileLookup( findex ).State                    
                      CSetting = ""
                          'print ReverseCfgFileLookup( findex ).Value
                          'print ReverseCfgFileLookup( findex ).State
                          'print ReverseCfgFileLookup( findex ).Config
                      findex = findex +1
                    End If
                  End If
                        
                end if

              LOOP
              Close #ffile
          Case 2  
            LogError "PIC-AS file not found " +  ChipPICASConfigFileV3
            LogError "Cannot continue with PIC-AS compilation"
            WriteErrorLog
            ErrorsFound = - 1
            end          
            Case 3: 
              LogError "PIC-AS file I/O error " +  ChipPICASConfigFileV3
              LogError "Cannot continue with PIC-AS compilation"
              WriteErrorLog
              ErrorsFound = - 1
              end
            Case 8: 
              LogError "No privileges (e.g., permission denied) " +  ChipPICASConfigFileV3
              LogError "Cannot continue with PIC-AS compilation"
              WriteErrorLog
              ErrorsFound = - 1
              end
            Case 13: 
              LogError "PIC-AS Termination request signal " +  ChipPICASConfigFileV3
              LogError "Cannot continue with PIC-AS compilation"
              WriteErrorLog
              ErrorsFound = - 1
              end
            Case 14: 
              LogError "PIC-AS Abnormal termination signal " +  ChipPICASConfigFileV3
              LogError "Cannot continue with PIC-AS compilation"
              WriteErrorLog
              ErrorsFound = - 1
              end
            Case 15: 
              LogError "PIC-AS Quit request signal " +  ChipPICASConfigFileV3
              LogError "Cannot continue with PIC-AS compilation"
              WriteErrorLog
              ErrorsFound = - 1
              end
          End Select
        Case 3: 
          LogError "PIC-AS file I/O error " +  ChipPICASConfigFile
          LogError "Cannot continue with PIC-AS compilation"
          WriteErrorLog
          ErrorsFound = - 1
          end
        Case 8: 
          LogError "No privileges (e.g., permission denied) " +  ChipPICASConfigFile
          LogError "Cannot continue with PIC-AS compilation"
          WriteErrorLog
          ErrorsFound = - 1
          end
        Case 13: 
          LogError "PIC-AS Termination request signal " +  ChipPICASConfigFile
          LogError "Cannot continue with PIC-AS compilation"
          WriteErrorLog
          ErrorsFound = - 1
          end
        Case 14: 
          LogError "PIC-AS Abnormal termination signal " +  ChipPICASConfigFile
          LogError "Cannot continue with PIC-AS compilation"
          WriteErrorLog
          ErrorsFound = - 1
          end
        Case 15: 
          LogError "PIC-AS Quit request signal " +  ChipPICASConfigFile
          LogError "Cannot continue with PIC-AS compilation"
          WriteErrorLog
          ErrorsFound = - 1
          end
      End Select
    'AS inc file not found, show error and quit
    '  LogError Message("PICASChipNotSupported")

  #ENDIF
  End Sub

Sub ReadChipData
  Dim As String TempData, TempList(20)
  Dim As String ReadDataMode, ChipDataFile, InLine, SFRName
  Dim As String PinName, PinDir, ConstName, ConstValue
  Dim As PinDirType Pointer PinDirData
  Dim As Integer TDC, CW, PD, FirstSFR, FindSFR
  Dim As Integer SFRLoc
  Dim As ConfigSetting Pointer ThisSetting
  Dim As SysVarType Pointer NewSysVar
  Dim As LinkedListElement Pointer CurrLoc
  Dim As AsmCommand Pointer NewAsmCommand, FindAsmCommand

  'Get filename
  #IFDEF __FB_UNIX__
    ChipDataFile = ID + "/chipdata/" + LCase(ChipName) + ".dat"
  #ELSE
    ChipDataFile = ID + "\chipdata\" + ChipName + ".dat"
  #ENDIF

  'Check that the chip data is present
  If OPEN(ChipDataFile For Input As #1) <> 0 Then
    'Chip file not found. If ChipName contains LF, try loading F file
    If InStr(LCase(ChipName), "lf") <> 0 Then
      TempData = ChipName
      Replace TempData, "lf", "f"

      #IFDEF __FB_UNIX__
        ChipDataFile = ID + "/chipdata/" + LCase(TempData) + ".dat"
      #ELSE
        ChipDataFile = ID + "\chipdata\" + TempData + ".dat"
      #EndIf
      If Open(ChipDataFile For Input As #1) = 0 Then GoTo ChipDataFileOpened
    End If

    'Chip data still not found, show error and quit
    LogError Message("ChipNotSupported")
    WriteErrorLog
    END
  End If
  ChipDataFileOpened:
  IF VBS = 1 THEN PRINT SPC(10); ChipDataFile

  FirstSFR = &HFFFF
  ChipFamilyVariant = 0
  ChipMinimumBankSelect = 0

  ReadDataMode = ""
  DO WHILE NOT EOF(1)
    LINE INPUT #1, InLine
    
    ' Remove leading TABs
    Do While left(InLine,1) = Chr(9)
      Replace( InLine, Chr(9), "")
    Loop

    InLine = Trim(LCase(InLine))
    IF InLine = "" THEN Goto ReadNextChipInfoLine
    IF Left(InLine, 1) = "'" THEN Goto ReadNextChipInfoLine
    IF Left(InLine, 1) = ";" THEN Goto ReadNextChipInfoLine
    IF Left(InLine, 2) = "\\" THEN Goto ReadNextChipInfoLine

    IF Left(InLine, 1) = "[" AND Right(InLine, 1) = "]" THEN
      ReadDataMode = InLine

    'Chip Data
    ElseIF ReadDataMode = "[chipdata]" AND INSTR(InLine, "=") <> 0 THEN
      TempData = Trim(Mid(InLine, INSTR(InLine, "=") + 1))
      ConstName = Trim(Left(InLine, INSTR(InLine, "=") - 1))
      ConstValue = TempData

      'Set appropriate internal variable
      Select Case ConstName
        Case "prog": ChipProg = VAL(TempData): ConstName = "WORDS"
        Case "eeprom": ChipEEPROM = Val(TempData)
        Case "ram": ChipRam = VAL(TempData)
        Case "i/o": ChipIO = Val(TempData): ConstName = "IO"
        Case "adc": ChipADC = Val(TempData)
        Case "maxmhz": ChipMaxSpeed = Val(TempData)
        Case "intosc"
          'ChipIntOsc = Val(TempData)
          IntOscSpeeds = 0
          Do While InStr(TempData, ",") <> 0
            IntOscSpeeds += 1
            IntOscSpeed(IntOscSpeeds) = Val(TempData)
            TempData = Trim(Mid(TempData, InStr(TempData, ",") + 1))
          Loop
          If TempData <> "" Then
            If Val(TempData) <> 0 Then
              IntOscSpeeds += 1
              IntOscSpeed(IntOscSpeeds) = Val(TempData)
            End If
          End If
          ConstValue = Str(IntOscSpeed(1))
        Case "pins": ChipPins = Val(TempData)
        Case "family": ChipFamily = Val(TempData)
        Case "familyvariant": ChipFamilyVariant = Val(TempData)
        Case "subfamily": ChipSubFamily = Val(TempData)
        Case "configwords": ConfWords = VAL(TempData)
        Case "psp": PSP = VAL(TempData)
        Case "maxaddress": MemSize = VAL(TempData)
        Case "gpr": ChipGPR = Val(TempData)
        Case "hardwaremult":
          HMult = 0: If TempData = "y" Then HMult = -1
          ConstValue = Str(-HMult)
        Case "nottested"
          If overridetestdatfilemessage = 0 then 
            If ( val(TempData) and 1 ) = 1 Then
              LogWarning "This is a development chip definition file (.DAT) and the chip has not been validated by the developers of the compiler. "
              LogWarning "    There may be errors in the ASM and/or the generated HEX file - please be aware that the libraries may or may not support this chip."+chr(13)+chr(10)
            End If

            If ( val(TempData) and 2 ) = 2 Then
              LogWarning "This is a development chip definition file (.DAT) and the chip and the interrupt section of the DAT is not completed."
              LogWarning "    Please resolve the interrupt section and contact the developers via the GCBASIC forum"+chr(13)+chr(10)
            End If
          End if
        Case "programmername":
          ChipProgrammerName = TempData

        Case "31ksupport"
        '31kSupport is exposed as Chip31kConfig, Chip31kRegister & Chip31kValue
        'Read four parameters

          _31kSupported.State = 0
          Do While _31kSupported.State <= 3

            _31kSupported.State += 1

            Select Case _31kSupported.State

              Case 1:
                _31kSupported.OSCType = Trim(Mid(TempData, 1, InStr(TempData, ",") - 1))
                AddConstant("CHIP" + UCase("31kConfig"), UCASE(_31kSupported.OSCType))

              Case 2:
                _31kSupported.OSCRegister = Trim(Mid(TempData,1, InStr(TempData, ",") - 1))
                AddConstant("CHIP" + UCase("31kRegister"), UCASE(_31kSupported.OSCRegister))

              Case 3:
                _31kSupported.Value = Val(TempData)
                AddConstant("CHIP" + UCase("31kValue"), str(_31kSupported.Value))

            End Select
            TempData = Trim(Mid(TempData, InStr(TempData, ",") + 1))
          Loop
          _31kSupported.State = -1

        Case "minimumbankselect": ChipMinimumBankSelect = Val(TempData)

        Case "memorylock": AddConstant("CHIP" + UCase(ConstName), ucase(ConstValue) )
        Case "avrdx": AddConstant("CHIP" + UCase(ConstName), ucase(ConstValue) ): ChipAVRDX = -1
      End Select

      'Create constant for data item
      if ConstName <> "memorylock" then

        If Instr(ConstValue, "0x") <> 0 Then 
          Replace ( ConstValue, "0x", "&H")
          ConstValue = Str(Val(ConstValue))
          Calculate( ConstValue )
          AddConstant("CHIP" + UCase(ConstName), ConstValue)
        Else
          AddConstant("CHIP" + UCase(ConstName), ConstValue)
        End If

      end if



      if Instr(ConstValue,"<<") or Instr(ConstValue,">>") Then
        ConstValue = Ucase(ConstValue)
        If InStr ( ConstValue, "0X" ) Then
          Replace ( ConstValue, "0X", "&H")
          Dim as Integer tmpValue, GtrPos, LesPos 
          Dim tmpConstValue as String
          If Instr(ConstValue,"<<") Then
            tmpConstValue = Trim ( Mid( ConstValue, Instr(ConstValue, "&H"), Instr( ConstValue, "<<" ) ) )
            LesPos = Instr( ConstValue, "<<" )
            tmpConstValue = Left( tmpConstValue, LesPos - 2 )
            replace ( ConstValue, tmpConstValue, Str(Val(tmpConstValue)) )
          Else
            LogError ( "Compiler does not support >> in [AVRMasks] section of the DAT file")
          End If

        End If

        ConstValue = Trim( Left( ConstValue, Instr( ConstValue, ";")-1) )        
        Calculate( ConstValue )
        AddConstant( UCase(ConstName), ConstValue )
      End if 

    ElseIF ReadDataMode = "[avrmasks]" AND INSTR(InLine, "=") <> 0 THEN
      'This reads the raw INC data and processes into constants.

      TempData = Trim(Mid(InLine, INSTR(InLine, "=") + 1))
      ConstName = Trim(Left(InLine, INSTR(InLine, "=") - 1))
      ConstValue = TempData

      if Instr(ConstValue,"<<") or Instr(ConstValue,">>") or Instr(Ucase(ConstName),"BP") > 0 or Instr(Ucase(ConstName),"BM") > 0 or Instr(Ucase(ConstName),"GM") > 0 Then
        ConstValue = Ucase(ConstValue)
        
        Replace ( ConstValue, "0X", "&H")
        Dim as Integer tmpValue, GtrPos, LesPos 
        Dim tmpConstValue as String
        If Instr(ConstValue,"<<") Then
          tmpConstValue = Trim ( Mid( ConstValue, Instr(ConstValue, "&H"), Instr( ConstValue, "<<" ) ) )
          LesPos = Instr( ConstValue, "<<" )
          tmpConstValue = Left( tmpConstValue, LesPos - 2 )
          replace ( ConstValue, tmpConstValue, Str(Val(tmpConstValue)) )
          ConstValue = Trim( Left( ConstValue, Instr( ConstValue, ";")-1) ) 

        ElseIf Instr(ConstValue,">>") then
          LogError ( "Compiler does not support >> in [AVRMasks] section of the DAT file")
        Else
          If Instr(ConstValue, "0X") Then 
            Replace ( ConstValue, "0X", "&H")
          End If
          ConstValue = Str(Val(ConstValue))
        End If

        Calculate( ConstValue )
        AddConstant( UCase(ConstName), ConstValue )
      End if 

    'Interrupts
    ElseIf ReadDataMode = "[interrupts]" AND INSTR(InLine, ":") <> 0 THEN
      IntCount += 1
      With Interrupts(IntCount)
        .EventName = Trim(Left(InLine, INSTR(InLine, ":") - 1))
        TDC = 0
        TempData = Trim(Mid(InLine, INSTR(InLine, ":") + 1))
        Do While Instr(TempData, ",") <> 0
          TDC += 1: TempList(TDC) = TRIM(Left(TempData, Instr(TempData, ",") - 1))
          TempData = Trim(Mid(TempData, Instr(TempData, ",") + 1))
        Loop
        If TempData <> "" Then
          TDC += 1: TempList(TDC) = TempData
        else
          'This may need constraining to MODEAVR with AVRDX
          'Added 1404 - prior to this.. if an interrupt had empty data then the old data was copied across
          TDC += 1: TempList(TDC) = ""
        End If

        If ModePIC Then
          .Vector = ""
          .VectorLoc = 0
          .EnableBit = TempList(1)
          .FlagBit = TempList(2)
          .Handler = ""

        ElseIf ModeAVR Then
          .Vector = TempList(1)
          .VectorLoc = Val(TempList(2))
          .EnableBit = TempList(3)
          .FlagBit = TempList(4)
          .Handler = ""

        ElseIf ModeZ8 Then
          .Vector = TempList(1)
          .VectorLoc = Val(TempList(2))
          .EnableBit = TempList(3)
          .FlagBit = TempList(4)
          .Handler = ""

        End If

      End With

    'AVR alaises
    ElseIf ReadDataMode = "[avralias]" AND INSTR(InLine, "=") <> 0 THEN
      InLine = UCase(InLine)
      SFRName = Trim(Left(InLine, INSTR(InLine, "=") - 1))
      SFRLoc = Val(Trim(Mid(InLine, INSTR(InLine, "=") + 1)))
      MakeSFR(SFRName, SFRLoc, -1 )

    'Registers
    ElseIf ReadDataMode = "[registers]" AND INSTR(InLine, ",") <> 0 THEN
      InLine = UCase(InLine)
      SFRName = Trim(Left(InLine, INSTR(InLine, ",") - 1))
      SFRLoc = Val(Trim(Mid(InLine, INSTR(InLine, ",") + 1)))
      MakeSFR(SFRName, SFRLoc, 0)

      'On 18F, need to find lowest SFR location
      If ChipFamily = 16 Then
        If SFRLoc < FirstSFR Then
          FirstSFR = SFRLoc
        End If
      End If

    'Bits
    ' 1 = name 3 = parent
    ' In file: name, parent, location
    ElseIf ReadDataMode = "[bits]" AND INSTR(InLine, ",") <> 0 THEN
      NewSysVar = Callocate(SizeOf(SysVarType))
      InLine = UCase(InLine)
      NewSysVar->Name = Trim(Left(InLine, INSTR(InLine, ",") - 1))
      TempData = Trim(Mid(InLine, INSTR(InLine, ",") + 1))
      NewSysVar->Parent = Trim(Left(TempData, INSTR(TempData, ",") - 1))
      NewSysVar->Location = Val(Trim(Mid(TempData, INSTR(TempData, ",") + 1)))
      HashMapSet(SysVarBits, NewSysVar->Name, NewSysVar)

    'FreeRAM
    ElseIf ReadDataMode = "[freeram]" AND INSTR(InLine, ":") <> 0 THEN
      MRC = MRC + 1
      MemRanges(MRC) = InLine

    'No bank RAM
    ElseIf ReadDataMode = "[nobankram]" And InStr(InLine, ":") <> 0 Then
      NoBankLocs += 1
      With NoBankLoc(NoBankLocs)
        .StartLoc = Val("&H" + Left(InLine, InStr(InLine, ":") - 1))
        .EndLoc = Val("&H" + Mid(InLine, InStr(InLine, ":") + 1))
      End With

    'Pinout data
    ElseIf Left(ReadDataMode, 6) = "[pins-" Then
      'Get first pin Function and data direction from line
      TempData = Mid(InLine, InStr(InLine, ",") + 1)
      If InStr(TempData, ",") <> 0 Then
        TempData = Trim(Left(TempData, InStr(TempData, ",") - 1))
      End If
      'Split pin name and pin direction
      If InStr(TempData, "(") <> 0 Then
        PinName = Left(TempData, InStr(TempData, "(") - 1)
        PinDir = Mid(TempData, InStr(TempData, "(") + 1)
        If InStr(PinDir, ")") <> 0 Then
          PinDir = Left(PinDir, InStr(PinDir, ")") - 1)
        End If
      Else
        PinName = TempData
        PinDir = "P"
      End If

      If IsIOPinName(PinName) Then
        If Left(PinName, 2) = "gp" Then
          PinName = "GPIO." + Mid(PinName, 3)
        Else
          PinName = "PORT" + Mid(PinName, 2, 1) + "." + Mid(PinName, 3, 1)
        End If
        PinDirData = GetPinDirection(PinName)
        If PinDirData <> 0 Then
          PinDirData -> AllowedDirections = PinDir
        End If
      End If

    'Config
    ElseIF ReadDataMode = "[config]" THEN
      InLine = UCase(InLine)
      If INSTR(InLine, ":") <> 0 THEN InLine = Left(InLine, INSTR(InLine, ":") - 1)
      IF INSTR(InLine, ",") <> 0 THEN
        COC = COC + 1
        With ConfigOps(COC)
          .Op = Trim(Left(InLine, INSTR(InLine, ",") - 1))
          InLine = Trim(Mid(InLine, INSTR(InLine, ",") + 1))
          .Loc = VAL(InLine)
          IF INSTR(InLine, ",") <> 0 THEN
            .Val = VAL(Trim(Mid(InLine, INSTR(InLine, ",") + 1)))
          END IF
        End With
      END IF

    'Config options
    ElseIf ReadDataMode = "[configops]" Then
      InLine = UCase(InLine)
      If InStr(InLine, "=") <> 0 Then
        'Create new option
        ThisSetting = Callocate(SizeOf(ConfigSetting))
        LinkedListInsert(ConfigSettings, ThisSetting)
        ThisSetting->Options = LinkedListCreate
        CurrLoc = ThisSetting->Options

        With (*ThisSetting)
          'Get option name
          .Name = Trim(Left(InLine, InStr(InLine, "=") - 1))

          'Get option values
          TempData = Trim(Mid(InLine, InStr(InLine, "=") + 1))
          Do While InStr(TempData, ",") <> 0
            CurrLoc = LinkedListInsert(CurrLoc, Trim(Left(TempData, InStr(TempData, ",") - 1)))
            TempData = Trim(Mid(TempData, InStr(TempData, ",") + 1))
          Loop
          If TempData <> "" Then
            CurrLoc = LinkedListInsert(CurrLoc, TempData)
          End If

        End With
      End If

    'Default config settings (18 only)
    ElseIF ReadDataMode = "[asmconfig]" THEN
      DCOC = DCOC + 1
      DefCONFIG(DCOC) = UCase(InLine)

    'Config masks (18 only)
    ElseIF ReadDataMode = "[configmask]" THEN
      ConfWords += 1
      ConfigMask(ConfWords) = VAL(InLine)

    END IF

    ReadNextChipInfoLine:
  LOOP
  CLOSE

  'From the AVRDx release the user can define interrupt as USART3_CTRLA.7 OR USART3_STATUS.USART_RXSIF_bp not just the BIT value
  'This routine changes USART3_STATUS.USART_RXSIF_bp to the bit value like USART3_STATUS.4
  'This may need constraining to ModeAVR
  'But, this now means the DAT file can support REGISTER_CONSTANT.BIT_CONSTANT
    Dim CurrVect as Integer
    For CurrVect = 1 to Ubound(Interrupts)
      With Interrupts(CurrVect)
          'Does the .EnableBit has a `.`
          If Instr(.EnableBit,".") > 0 Then
            If Trim(Mid( .EnableBit, Instr(.EnableBit,".")+1 )) = Str(Val(Trim(Mid( .EnableBit, Instr(.EnableBit,".")+1 )))) Then 'A numberic value that is ok
              'Ensure a numeric constant is correct
              .EnableBit = Left( .EnableBit, Instr(.EnableBit,".")) + Str(Val(Trim(Mid( .EnableBit, Instr(.EnableBit,".")+1 ))))
            ElseIF HashMapGet(Constants, UCase( Mid( .EnableBit, Instr(.EnableBit,".")+1 ) )) <> 0 Then
              ' If the BIT_CONSTANT exists then transform to numeric.  
              ' We know the BIT_CONSTANT exists as HashMapGet() returns 0 when non existant CONSTANT string is passed
              ' So, now do a HashMapGetStr() to get the value of the specific .EnableConstant
              ' All these constants are in the DAT file
              .EnableBit =   Left( .EnableBit, Instr(.EnableBit,".")) + Str(Val(HashMapGetStr(Constants, UCase( Mid( .EnableBit, Instr(.EnableBit,".")+1 ) ))))
            End if
          End If
          
          '.FlagBit - treated same as 'EnableBit
          If Instr(.FlagBit,".") > 0 Then
            If Trim(Mid( .FlagBit, Instr(.FlagBit,".")+1 )) = Str(Val(Trim(Mid( .FlagBit, Instr(.FlagBit,".")+1 )))) Then
              .FlagBit = Left( .FlagBit, Instr(.FlagBit,".")) + Str(Val(Trim(Mid( .FlagBit, Instr(.FlagBit,".")+1 ))))
            ElseIf  HashMapGet(Constants, UCase( Mid( .FlagBit, Instr(.FlagBit,".")+1 ) )) <> 0 Then
              .FlagBit = Left( .FlagBit, Instr(.FlagBit,".")) + Str(Val(HashMapGetStr(Constants, UCase( Mid( .FlagBit, Instr(.FlagBit,".")+1 ) ))))
            End If
          End If
      End With
    Next

  'Misc initialisation
  If ModePIC Then
    'Calculate the number of high PC bits
    PCUpper = 0
    IF ChipFamily = 12 Then
      If ChipProg > 512 THEN PCUpper = 1
      If ChipProg > 1024 THEN PCUpper = 2
    ElseIf ChipFamily = 14 Or ChipFamily = 15 Then
      If ChipProg > 2048 THEN PCUpper = 1
      IF ChipProg > 4096 THEN PCUpper = 2
    END If

    'If there are no bank free ram locations defined, guess them
    'Typically expect 0:F on 12 bit, 70:7F on 14, and 0:5F on 16
    If NoBankLocs = 0 Then
      Select Case ChipFamily
        Case 12:
          NoBankLocs = 1
          With NoBankLoc(1)
            .StartLoc = 0
            .EndLoc = &HF
          End With
        Case 14, 15:
          NoBankLocs = 1
          With NoBankLoc(1)
            .StartLoc = &H70
            .EndLoc = &H7F
          End With
        Case 16:
          If MemSize >= 4096 Then
            '18F with larger memory ('K42 and others)
            'Guess where SFR access bank starts
            'Not earlier than 0xF60, but can be later (0xF80 on 18F2620, for example)
            If FirstSFR < &H3F60 Then FirstSFR = &H3F60

            NoBankLocs = 2
            With NoBankLoc(1)
              .StartLoc = 0
              .EndLoc = (FirstSFR - 1) And 255
            End With
            'Access RAM for SFRs
            With NoBankLoc(2)
              .StartLoc = FirstSFR
              .EndLoc = &H3FFF
            End With

          Else
            'Older 18F with smaller RAM
            'Guess where SFR access bank starts
            'Not earlier than 0xF60, but can be later (0xF80 on 18F2620, for example)
            If FirstSFR < &HF60 Then FirstSFR = &HF60

            'Assume that SFR access bank starts at a location that is a multiple of 16
            FirstSFR = FirstSFR And &HFF0

            NoBankLocs = 2
            With NoBankLoc(1)
              .StartLoc = 0
              .EndLoc = (FirstSFR - 1) And 255
            End With
            'Access RAM for SFRs
            With NoBankLoc(2)
              .StartLoc = FirstSFR
              .EndLoc = &HFFF
            End With
          End If

      End Select

      'If on an 18F and there is only one set of non-banked locations, add SFR
      If ChipFamily = 16 And NoBankLocs = 1 Then
        NoBankLocs = 2
        With NoBankLoc(2)
          .StartLoc = NoBankLoc(1).StartLoc + &HF01
          .EndLoc = &HFFF
        End With
      End If
    End If

  ElseIf ModeAVR Then

  End If

  'Load assembly commands

  #IFDEF __FB_UNIX__
    ChipDataFile = ID + "/chipdata/core" + Str(ChipFamily) + ".dat"
  #Else
    ChipDataFile = ID + "\chipdata\core" + Str(ChipFamily) + ".dat"
  #ENDIF
  If OPEN(ChipDataFile For INPUT AS #1) <> 0 Then
    TempData = Message("MissingCoreFile")
    Replace TempData, "%core%", ChipDataFile
    Replace TempData, "%chip%", ChipName
    LogError TempData
    WriteErrorLog
    END
  End If

  DO WHILE NOT EOF(1)
    LINE INPUT #1, InLine
    InLine = Trim(UCase(InLine))
    IF InLine <> "" AND Left(InLine, 1) <> "'" THEN
      DO WHILE INSTR(InLine, Chr(9)) <> 0: Replace InLine, Chr(9), " ": LOOP

      NewAsmCommand = Callocate(SizeOf(AsmCommand))

      With *NewAsmCommand

        'Get syntax and number of words
        .Syntax = Trim(Left(InLine, INSTR(InLine, ";") - 1))
        InLine = Mid(InLine, INSTR(InLine, ";") + 1)
        .Words = VAL(Trim(Left(InLine, INSTR(InLine, ";") - 1)))
        InLine = Mid(InLine, INSTR(InLine, ";") + 1)

        'Get binary instruction words and applicable variants
        CW = 0
        Do While INSTR(InLine, ";") <> 0
          If CW < .Words Then
            CW += 1
            .Word(CW) = TRIM(Left(InLine, INSTR(InLine, ";") - 1))
          Else
            .FamilyVariants += 1
            .FamilyVariant(.FamilyVariants) = Val(TRIM(Left(InLine, INSTR(InLine, ";") - 1)))
          End If
          InLine = Trim(Mid(InLine, INSTR(InLine, ";") + 1))
        Loop
        If InLine <> "" Then
          If CW < .Words Then
            CW += 1
            .Word(CW) = InLine
          Else
            .FamilyVariants += 1
            .FamilyVariant(.FamilyVariants) = Val(InLine)
          End If
        End If

        'Get command name
        .Cmd = Trim(UCase(.Syntax))
        If INSTR(.Cmd, " ") <> 0 THEN .Cmd = Trim(Left(.Cmd, INSTR(.Cmd, " ") - 1))

        'Get parameters
        Dim As String ParamName
        ParamName = .Syntax
        IF INSTR(ParamName, " ") = 0 Then
          ParamName = ""
        Else
          ParamName = Trim(Mid(ParamName, INSTR(ParamName, " ") + 1))
        End If
        GetTokens (ParamName, .Param(), .Params, ",")

      End With

      'Is command with same name already in hashmap?
      FindAsmCommand = HashMapGet(ASMCommands, NewAsmCommand->Cmd)
      If FindAsmCommand <> 0 Then
        'Yes, so add new command to existing one
        Do While FindAsmCommand->Alternative <> 0
          FindAsmCommand = FindAsmCommand->Alternative
        Loop
        FindAsmCommand->Alternative = NewAsmCommand
      Else
        'No, so add to map normally
        HashMapSet(ASMCommands, NewAsmCommand->Cmd, NewAsmCommand)
      End If

    END If
  Loop
  CLOSE

  'Check for presence of High FSR bit
  HighFSR = 0
  If ChipFamily = 12 Or ChipFamily = 14 Then
    HighFSR = HasSFRBit("IRP")
  End If

  'Check for LAT registers
  If ChipFamily <> 15 And ChipFamily <> 16 Then
    UseChipOutLatches = 0
  ElseIf Not (HasSFR("LATA") OrElse HasSFR("LATB") OrElse HasSFR("LATC")) Then
    UseChipOutLatches = 0
  End If

  'Ensure GIE is switched in glitch free mode
  If ModePIC Then
    NewSysVar = HashMapGet(SysVarBits, "GIE")
    If NewSysVar <> 0 Then
      HashMapSet(GlitchFreeOutputs, NewSysVar->Parent + "." + Str(NewSysVar->Location), "y")
    End If
  End If

  'Check for high table pointer bytes
  HighTBLPTRBytes = 0
  If ChipFamily = 16 Then
    'The Q10 has SECTOR RAM and therefore "TBLPTRU" must be managed
    If ( ChipProg > 32767 or  ( Instr( UCase(ChipName) , "Q10") <> 0 ) ) And HasSFR("TBLPTRU") Then
      HighTBLPTRBytes = 3
    Else
      HighTBLPTRBytes = 2
    End If
  End If
  
  End Sub

Sub ReadOptions(OptionsIn As String)
  'Process #option statements

  Dim As String OutMessage, VolatileVar, RequiredVar
  Dim As LinkedListElement Pointer OptionElements, CurrElement
  Dim As String VarName, VarBit, MandatedStringMessage
  Dim As SysVarType Pointer SysVarBit
  Dim As Integer VarBitNo, TempVal

  'Set defaults
  Bootloader = 0

  'Get settings
  OptionElements = GetElements(OptionsIn, " ,")

  CurrElement = OptionElements->Next
  Do While CurrElement <> 0

    'Ignore Explicit (should have been read earlier)
    If CurrElement->Value = "EXPLICIT" Then
      'Do nothing

    'Get bootloader setting
    ElseIf CurrElement->Value = "BOOTLOADER" Then
      If  CurrElement->Next <> 0 Then
        If IsConst(CurrElement->Next->Value) Then
          Bootloader = MakeDec(CurrElement->Next->Value)
          CurrElement = CurrElement->Next
        End If
      End If

    'Reserve memory at the end of flash for HEF/bootloader?
    ElseIf CurrElement->Value = "RESERVEHIGHPROG" Then

      If CurrElement->Next <> 0 Then
        If IsConst(CurrElement->Next->Value) Then
          TempVal = Val(CurrElement->Next->Value)
        Else 
          TempVal = Val(HashMapGetStr(Constants, UCase(CurrElement->Next->Value)))
        End if
        If TempVal > ReserveHighProg Then
          ReserveHighProg = TempVal
        End If

        CurrElement = CurrElement->Next
      End If

    'Disable automatic use of output latches?
    ElseIf CurrElement->Value = "NOLATCH" Then
      UseChipOutLatches = 0

    'Disable automatic interrupt context save/restore?
    ElseIf CurrElement->Value = "NOCONTEXTSAVE" Then
      AutoContextSave = 0

    'Disable config (Used for TinyBootloader Support, possibly others)
    ElseIf CurrElement->Value = "NOCONFIG" Then
      ConfigDisabled = -1

    'Disable INITSYS and BASPROGRAMEND
    ElseIf CurrElement->Value = "USERCODEONLY" Then
      UserCodeOnlyEnabled = -1
      If InStr(CurrElement->Next->Value,":") <> 0 Then
          UserDefineStartLabel = Trim(Mid(CurrElement->Next->Value,1,  InStr(CurrElement->Next->Value,":")-1))
          CurrElement = CurrElement->Next
      Else
          OutMessage = Message("WarningUserCodeLabelIncorect")
          LogWarning OutMessage, ""
      End If

    'Volatile bit?
    ElseIf CurrElement->Value = "VOLATILE" Then
      If CurrElement->Next <> 0 Then
        VolatileVar = ReplaceConstantsLine(CurrElement->Next->Value, 0)

        'Split dest into var and bit, get source
        VarName = ""
        If InStr(VolatileVar, ".") <> 0 Then
          VarName = Trim(Left(VolatileVar, InStr(VolatileVar, ".") - 1))
          VarBit = Trim(Mid(VolatileVar, InStr(VolatileVar, ".") + 1))

          'Get name for map
          VarBitNo = -1
          If IsConst(VarBit) Then
            VarBitNo = MakeDec(VarBit)
          Else
            SysVarBit = HashMapGet(SysVarBits, VarBit)
            If SysVarBit <> 0 Then
              VarBitNo = SysVarBit->Location
            End If
          End If
          If VarBitNo <> -1 Then
            HashMapSet(GlitchFreeOutputs, VarName + "." + Str(VarBitNo), "y")
          Else
            OutMessage = Message("WarningVolatileBit")
            Replace OutMessage, "%bit%", VolatileVar
            LogWarning OutMessage, ""
          End If
        Else
          OutMessage = Message("WarningVolatileBit")
          Replace OutMessage, "%bit%", VolatileVar
          LogWarning OutMessage, ""
        End If

        CurrElement = CurrElement->Next
      End If

    'Unrecognised option
    Else
      OutMessage = Message("WarningBadOption")
      Replace OutMessage, "%option%", CurrElement->Value
      LogError (OutMessage, "")
    End If

    CurrElement = CurrElement->Next
  Loop
  
  End Sub

Sub RecordSubCall(CompSub As SubType Pointer, CalledSub As SubType Pointer)

  Dim FindCall As LinkedListElement Pointer

  If CompSub = 0 Then Exit Sub
  If CalledSub = 0 Then Exit Sub

  'Record outgoing call in CompSub
  With *CompSub
    'Check for Sub in list already
    FindCall = .CallList->Next
    Do While FindCall <> 0
      If FindCall->MetaData = CalledSub Then
        FindCall->NumVal += 1
        Exit Sub
      End If
      FindCall = FindCall->Next
    Loop

    'Sub not in list, add it
    '.Call(.Calls) = CalledSub
    FindCall = LinkedListInsert(.CallList, CalledSub)
    FindCall->NumVal = 1
  End With


  'Record incoming call in CalledSub
  With *CalledSub
    'Check for Sub in list already
    FindCall = .CallerList->Next
    Do While FindCall <> 0
      If FindCall->MetaData = CompSub Then
        FindCall->NumVal += 1
        Exit Sub
      End If
      FindCall = FindCall->Next
    Loop

    'Sub not in list, add it
    '.Call(.Calls) = CalledSub
    FindCall = LinkedListInsert(.CallerList, CompSub)
    FindCall->NumVal = 1
  End With
  
  End Sub

Function ReplaceFnNames(InName As String) As String
  'Replace tokens in Sub names with actual names

  Dim As String OutName
  Dim As Integer Temp
  OutName = InName

  'Function name
  Do While Instr(OutName, Chr(31)) <> 0
    Temp = VAL(MID(OutName, Instr(OutName, Chr(31)) + 1))
    Replace OutName, Chr(31) + Str(Temp) + Chr(31), Subroutine(Temp)->Name
  Loop

  'Function name (used differently to 31)
  Do While Instr(OutName, Chr(30)) <> 0
    Temp = VAL(MID(OutName, Instr(OutName, Chr(30)) + 1))
    Replace OutName, Chr(30) + Str(Temp) + Chr(30), Subroutine(Temp)->Name
  Loop

  'Miscellaneous item. Used for array names, may be used for other things
  Do While Instr(OutName, Chr(29)) <> 0
    Temp = VAL(MID(OutName, Instr(OutName, Chr(29)) + 1))
    Replace OutName, Chr(29) + Str(Temp) + Chr(29), PreserveCode(Temp)
  Loop

  Return OutName
  End Function

Function RequestSub(Requester As SubType Pointer, SubNameIn As String, SubSigIn As String = "") As Integer
  'Will return index of Sub in Subroutine
  'Requester is used to log which Sub calls which
  'If Requester is 0, it will be set to main sub
  'If Sub not found, will return -1

  Dim As Integer CurrSub, BestMatchPos, BestMatch, ThisMatch, InReqList
  Dim As String SubName, SubSig
  Dim As LinkedListElement Pointer ReqListPos, ReqListData

  SubName = UCase(Trim(SubNameIn))
  BestMatchPos = -1
  BestMatch = -1

  If Requester = 0 Then
    Requester = Subroutine(0)
  End If

  'If no Sub being called, exit before recording attempt
  If SubName = "" Then
    Return -1
  End If

  For CurrSub = 1 To SBC
    If UCase(Trim(Subroutine(CurrSub)->Name)) = SubName Then
      If Subroutine(CurrSub)->Overloaded Then
        ThisMatch = SubSigMatch(GetSubSig(*Subroutine(CurrSub)), SubSigIn)
        If ThisMatch > BestMatch Then
          BestMatch = ThisMatch
          BestMatchPos = CurrSub
        End If
      Else
        Subroutine(CurrSub)->Required = -1
        RecordSubCall(Requester, Subroutine(CurrSub))

        Return CurrSub
      End If
    End If
  Next

  If BestMatch <> -1 Then
    Subroutine(BestMatchPos)->Required = -1
    RecordSubCall(Requester, Subroutine(BestMatchPos))
    Return BestMatchPos
  End If

  'Record failed attempted call
  'May be able to grant request later
  'Only add to list if not already in it
  ReqListPos = AttemptedCallList->Next
  InReqList = 0
  Do While ReqListPos <> 0
    With *ReqListPos
      If .MetaData <> 0 Then
        ReqListData = .MetaData
        'Put Sub name in .Value, Sub sig in .MetaData->Value, caller in .MetaData->MetaData
        If ReqListData->MetaData = Requester Then
          If LCase(.Value) = LCase(SubName) And LCase(ReqListData->Value) = LCase(SubSigIn) Then
            InReqList = -1
            Exit Do
          End If
        End If
      End If
    End With
    ReqListPos = ReqListPos->Next
  Loop

  'Not in requested list, add
  If Not InReqList Then
    ReqListPos = LinkedListInsert(AttemptedCallList, SubName)
    ReqListData = LinkedListCreate
    ReqListPos->MetaData = ReqListData
    ReqListData->Value = SubSigIn
    ReqListData->MetaData = Requester
  End If

  Return -1
  End Function

Sub RetrySubRequests
  'Check AttemptedCallList for calls that could not be recorded
  'Attempt to record them

  Dim As LinkedListElement Pointer ReqListPos, ReqListData

  ReqListPos = AttemptedCallList->Next
  Do While ReqListPos <> 0

    If ReqListPos->MetaData <> 0 Then
      ReqListData = ReqListPos->MetaData
      RequestSub(ReqListData->MetaData, ReqListPos->Value, ReqListData->Value)
    End If

    ReqListPos = ReqListPos->Next
  Loop
  
  End Sub

Sub SetCalcTempType (CalcVar As String, NewType As String)

  'Don't allow calc vars to be bit
  If UCase(NewType) = "BIT" Then NewType = "BYTE"

  Select Case UCASE(RIGHT(TRIM(CalcVar), 1))
    Case "A": CalcTempType(1) = UCASE(TRIM(NewType))
    Case "B": CalcTempType(2) = UCASE(TRIM(NewType))
    Case "X": CalcTempType(3) = UCASE(TRIM(NewType))
  End Select
  
  End Sub

Function SetStringPointers (V1 As String, V2 As String, CurrSub As SubType Pointer, Origin As String) As LinkedListElement Pointer

  Dim As String Temp

  'Prepare output list
  Dim As LinkedListElement Pointer OutList, CurrLine
  OutList = LinkedListCreate
  CurrLine = OutList

  'Remove $ from end of var name
  IF Right(V1, 1) = "$" Then V1 = Left(V1, LEN(V1) - 1)
  IF Right(V2, 1) = "$" Then V2 = Left(V2, LEN(V2) - 1)

  'Check input
  If INSTR(UCase(V1), ";STRING") <> 0 Then
    LogError Message("BadStringConst"), Origin
    Return OutList
  End If
  'If V2 is a string, put it into a temp array
  If INSTR(UCase(V2), ";STRING") <> 0 Then
    AddVar("SysTempArray", "STRING", 20, 0, "REAL", Origin, , -1)
    'CSC += 1: TempData(CSC) = "SYSTEMPARRAY()=" + V2
    CurrLine = LinkedListInsert(CurrLine, "SYSTEMPARRAY()=" + V2)
    V2 = "SYSTEMPARRAY"
  End If

  'Add code to set pointers
  'Load V1 into 0, V2 into 1
  CurrLine = LinkedListInsertList(CurrLine, GenerateArrayPointerSet(V1, 0, CurrSub, Origin))
  CurrLine = LinkedListInsertList(CurrLine, GenerateArrayPointerSet(V2, 1, CurrSub, Origin))

  Return OutList
  End Function

Sub ShowBlock (BlockIn As String)

  Dim As String Block, Temp, LineIn, DispLine
  Dim As Integer InBlock
  Block = UCase(Trim(BlockIn))

  #IFDEF __FB_UNIX__
    OPEN ID + "/messages.dat" FOR INPUT AS #9
  #ELSE
    OPEN ID + "\messages.dat" FOR INPUT AS #9
  #ENDIF

  InBlock = 0
  DO WHILE NOT EOF(9)
    LINE INPUT #9, LineIn
    DispLine = LineIn
    LineIn = UCase(Trim(LineIn))
    IF LineIn = "BLOCK " + Block THEN
      InBlock = -1
    ElseIf InBlock Then
      If LineIn = "END BLOCK" THEN
        Exit Do
      Else
        Do While INSTR(UCase(DispLine), "%VERSION%") <> 0: Replace DispLine, "%VERSION%", Version: Loop
        Do While INSTR(UCase(DispLine), "%FILENAME%") <> 0: Replace DispLine, "%FILENAME%", FI: Loop
        Do While INSTR(UCase(DispLine), "%OUTPUTFILE%") <> 0: Replace DispLine, "%OUTPUTFILE%", OFI: Loop
        PRINT DispLine
      End If
    End If
  LOOP
  CLOSE #9
  
  End Sub

Sub SplitLines (CompSub As SubType Pointer)

  Dim As String Temp, LastHalf
  Dim As LinkedListElement Pointer CurrLine

  'Search main program
  CurrLine = CompSub->CodeStart->Next
  Do While CurrLine <> 0
    IF INSTR(CurrLine->Value, ":") <> 0 AND Right(CurrLine->Value, 1) <> ":" THEN
      Temp = CurrLine->Value
      LastHalf = LTrim(Mid(Temp, INSTR(Temp, ":") + 1))
      CurrLine->Value = RTrim(Left(Temp, INSTR(Temp, ":") - 1))
      LinkedListInsert(CurrLine, LastHalf)
    Else
      CurrLine = CurrLine->Next
    End If
  Loop
  
  End Sub

Function TempRemove(Removed As String) As String
  'Temporarily remove something from a line. Can be replaced later with ReplaceFnNames

  'Record removed item
  PCC += 1: PreserveCode(PCC) = Removed

  Return Chr(29) + Str(PCC) + Chr(29)  
  End Function

Sub TidyProgram
  Dim As Integer CurrSub

  For CurrSub = 0 To SBC
    With *Subroutine(CurrSub)
      If .Required Then
        TidySubroutine(Subroutine(CurrSub))
      End If
    End With
  Next
  
  End Sub

Sub TidySubroutine(CompSub As SubType Pointer)

  'Fix Function calls
  FixFunctions(CompSub)

  'Set Bank
  AddBankCommands(CompSub)

  'Perform some simple ASM optimisations
  AsmOptimiser(CompSub)

  'Add banking mode to 18F commands
  If ChipFamily = 16 Then
    Add18FBanks(CompSub)
  End If

  'Tidy up IFs
  OptimiseIF (CompSub)

  'Replace incf/btfss Z with incfsz (PIC)
  OptimiseIncrement (CompSub)
  
  End Sub

Function TranslateFile(InFile As String) As String
  'Translates file to GCBASIC format

  Dim As String OutFile, ConvFile, CurrType, ConvExe, ConvParams, PathDiv
  Dim As String ConvDir, Temp
  Dim As Integer Converted, Converter, CurrForm, ConvertRequired
  Dim As Double InDate, OutDate
  OutFile = InFile

  'Get converter directory
  #Ifdef __FB_UNIX__
    ConvDir = ID + "/converters"
    PathDiv = "/"
  #ELSE
    ConvDir = ID + "\converters"
    PathDiv = "\"
  #EndIf

  'Might need to do multiple conversions
  Do
    Converted = 0

    'Find converter for current type
    CurrType = Trim(LCase(Mid(OutFile, InStrRev(OutFile, ".") + 1)))
    Converter = -1
    For Converter = 1 To FileConverters
      With FileConverter(Converter)
        'Check all formats for current converter
        For CurrForm = 1 To .InFormats
          If .InFormat(CurrForm) = CurrType Then
            'Have found a converter
            'Check for converted file
            ConvFile = ReplaceToolVariables("%filename%", .OutFormat, OutFile)
            ConvertRequired = 0
            'If no converted file, need to convert
            If Dir(ConvFile) = "" Then
              ConvertRequired = -1
            'If converted file older than source file, need to convert
            Else
              InDate = FileDateTime(OutFile)
              OutDate = FileDateTime(ConvFile)
              If InDate > OutDate Then ConvertRequired = -1
            End If

            'If DeleteTarget set, always convert
            If .DeleteTarget Then ConvertRequired = -1

            If VBS = 1 Then
              Temp = Message("Converting")
              Replace Temp, "%infile%", OutFile
              Print Spc(10); Temp;
            End If

            'If convert needed, convert
            If ConvertRequired Then
              ConvParams = InFile
              If .Params <> "" Then
                ConvParams = ConvParams+" "+ReplaceToolVariables(.Params, CurrType, InFile)
                'If chip model required but not known, cannot convert
                If InStr(LCase(.Params), "%chipmodel%") <> 0 And ChipName = "" Then
                  Return ""
                End If
              End If
              ConvExe = AddFullPath(.ExeName, ConvDir)
              If Exec(ConvExe, ConvParams) <> -1 Then
                If VBS = 1 Then Print Message("Success")
                OutFile = ConvFile
                Converted = -1
              Else
                Print Message("NoConverter")
              End If
            Else
              If VBS = 1 Then Print Message("ConvertedAlready")
              OutFile = ConvFile
              Converted = -1
            End If

            GoTo CheckNextFormat
          End If
        Next
      End With
    Next

    CheckNextFormat:
  Loop While Converted

  Return OutFile
  End Function

Sub ValueChanged(VarName As String, VarValue As String)

  If VarName <> "" Then Exit Sub
  IF Left(UCase(VarName), 7) <> "SYSTEMP" Then Exit Sub

  CV = VAL(Mid(Trim(VarName), 8))

  CalcVars(CV).CurrentValue = VarValue
  End Sub

Sub WriteAssembly
  Dim As String Temp, VarDecLine, PICASVarDecLine, AddedBits, BitName
  Dim As Integer PD, AddSFR, FindSREG, legacyConfigPublished
  Dim As LinkedListElement Pointer CurrLine
  Dim As LinkedListElement Pointer VarList
  Dim As LinkedListElement Pointer FinalConstantList 
  Dim As SysVarType Pointer SysVar

  'Force AVR to use GCASM if PIC-AS, or MPASM selected .. this is not supported
  If AFISupport = 1 Then
      If ModeAVR and AsmExe <> "GCASM" Then
          AsmExe = "GCASM"
          AFISupport = 0
          Print Message("SwappingAssembertoGCASMs")
      End if
  End IF

  If  ( UCASE(AsmExe) = "PIC-AS" or Instr(UCASE(AsmExe), "MPASM" ) > 0 ) and ModeAVR Then
    AsmExe = "gcasm"
    Print Message("SwappingAssembertoGCASMs")
  End if

  'Write .ASM program
  On error goto ASMLocked
  OPEN OFI FOR OUTPUT AS #1
  If err <> 0 then
      ASMLocked:
      LogError "ASM File locked/open error. Cannot create output ASM", ""
      ErrorsFound = -1
      exit Sub
  End if
  On Error goto 0

  if AFISupport = 1 then
      On error goto ASLocked
      OPEN AFI FOR OUTPUT AS #2
      If err <> 0 then
          ASLocked:
          LogError "AS File locked/open error. Cannot create output AS", ""
          ErrorsFound = -1
          exit Sub
      End if
      On Error goto 0
  Else
      Kill AFI
  End if

  ' ASM file
  If ModePIC Then
      PRINT #1, ";Program compiled by GCBASIC (" + Version + ") for Microchip MPASM/MPLAB-X Assembler using " +  __FB_SIGNATURE__ + "/" + __DATE_ISO__+" CRC"+STR(ReservedwordC)
  Else
      PRINT #1, ";Program compiled by GCBASIC (" + Version + ") for Microchip AVR Assembler using " +  __FB_SIGNATURE__ + "/" + __DATE_ISO__+" CRC"+STR(ReservedwordC)
  End If
  Print #1, ";Need help? "
  Print #1, ";  Please donate to help support the operational costs of the project.  Donate via https://gcbasic.com/donate/"
  Print #1, ";  "
  Print #1, ";  See the GCBASIC forums at http://sourceforge.net/projects/gcbasic/forums,"
  Print #1, ";  Check the documentation and Help at http://gcbasic.sourceforge.net/help/,"
  Print #1, ";or, email us:"
  Print #1, ";   evanvennn at users dot sourceforge dot net"
  PRINT #1, Star80

  if AFISupport = 1 then
      ' AS file
      PRINT #2, ";Program compiled by GCBASIC (" + Version + ") for Microchip PIC-AS using " +  __FB_SIGNATURE__ + "/" + __DATE_ISO__+" CRC"+STR(ReservedwordC)
      Print #2, ";  Please donate to help support the operational costs of the project.  Donate via https://gcbasic.com/donate/"
      Print #2, ";  "
      Print #2, ";  See the GCBASIC forums at http://sourceforge.net/projects/gcbasic/forums,"
      Print #2, ";  Check the documentation and Help at http://gcbasic.sourceforge.net/help/,"
      Print #2, ";or, email:"
      Print #2, ";   evanvennn at users dot sourceforge dot net"
      PRINT #2, Star80
      Print #2, ";   Installation Dir : " + ID
      Print #2, ";   Source file      : " + SourceFile(1).FileName
      Print #2, ";   Setting file     : " + globalSettingsFile
      Print #2, ";   Preserve mode    : " + str(PreserveMode)
      Print #2, ";   Assembler        : " + ASMEXE
      Print #2, ";   Programmer       : " + PrgExe
      Print #2, ";   Output file      : " + OFI
      Print #2, ";   Float Capability : " + VersionSuffix
      If RequiredModuleCheck = 0  then Print #2, ";   Option Required  : Disabled "
      Print #2, Star80
  
  End if

  Print #1, ";   Installation Dir : " + ID
  Print #1, ";   Source file      : " + SourceFile(1).FileName
  Print #1, ";   Setting file     : " + globalSettingsFile
  Print #1, ";   Preserve mode    : " + str(PreserveMode)
  Print #1, ";   Assembler        : " + ASMEXE
  Print #1, ";   Programmer       : " + PrgExe
  Print #1, ";   Output file      : " + OFI
  Print #1, ";   Float Capability : " + VersionSuffix
  If  RequiredModuleCheck = 0 then Print #1, ";   Option Required  : Disabled "
  Print #1, Star80
  Print #1, ""
  If ModePIC Then
    'asm
    PRINT #1, ";Set up the assembler options (Chip type, clock source, other bits and pieces)"
    PRINT #1, " LIST p=" + ChipName + ", r=DEC"
    PRINT #1, " TITLE       "+ CHR(34)+SourceFile(1).FileName+CHR(34)
    PRINT #1, " SUBTITLE    "+ CHR(34)+date+CHR(32)+time+CHR(34)
    'Workaround for 16C5x chips
    If Len(ChipName) = 5 And Left(ChipName, 2) = "16" And Mid(ChipName, 4, 1) = "5" Then
      PRINT #1, "#include <P16" + Mid(ChipName, 3, 1) + "5X.inc>"
    Else
      PRINT #1, "#include <P" + ChipName + ".inc>"
    End If

      if AFISupport = 1 then
          'as
          PRINT #2, ""
          PRINT #2, ";Set up the assembler options (Chip type, clock source, other bits and pieces)"
          PRINT #2, ";PROCESSOR   " + ChipName
          PRINT #2, " PAGEWIDTH   180"
          PRINT #2, " RADIX       DEC"
          PRINT #2, " TITLE       "+ CHR(34)+SourceFile(1).FileName+CHR(34)
          PRINT #2, " SUBTITLE    "+ CHR(34)+date+CHR(32)+time+CHR(34)
          PRINT #2, ""
          PRINT #2, "; Reverse lookup file(s)"
          PRINT #2, "; "+ChipPICASDataFile
          PRINT #2, "; "+ChipPICASConfigFile
          PRINT #2, "; Alternative CONFIG file is to be found at C:\Program Files\Microchip\xc8\vX.YY\pic\dat\cfgdata as per Microchip support ticket #00730936 "

          PRINT #2, ""
          PRINT #2, Star80
          If ChipFamily = 16 Then
            PRINT #2, ";Explicit PIC-AS constants to resolve the crazyness of the PIC-AS syntax"
            PRINT #2, ";These are therefore the same as MPASM"
            PRINT #2, "#define BANKED b"
            PRINT #2, "#define ACCESS a"
            PRINT #2, "#define UPPER low highword"
          End if
          'if development workarounds... meant to be resolved at PIC-AS 2.35
          '          PRINT #2, ""
          '          PRINT #2, ";#define BTFSS mybtfss"
          '          PRINT #2, ";#define BTFSS mybtfsc"
          '
          '          PRINT #2, ""
          '          PRINT #2, "mybtfss MACRO arg1, arg2"
          '          PRINT #2, "local arg3, arg4"
          '          PRINT #2, " arg3 set ( arg2 << 7 )"
          '          PRINT #2, " arg4 set ( arg1 AND 127 )"
          '          PRINT #2, " dw arg3 + arg4 + 0x1C00"
          '          PRINT #2, "ENDM"
          '          PRINT #2, ""
          '          PRINT #2, "mybtfsc MACRO arg1, arg2"
          '          PRINT #2, "local arg3, arg4"
          '          PRINT #2, " arg3 set ( arg2 << 7 )"
          '          PRINT #2, " arg4 set ( arg1 AND 127 )"
          '          PRINT #2, " dw arg3 + arg4 + 0x1800"
          '          PRINT #2, "ENDM"
          '          PRINT #2, ""

            If ChipFamily = 12 Or ChipFamily = 14 and HighFSR Then
              ' Only create macro for old version of PIC-AS
              If Instr(Ucase(ChipPICASRoot),"2.32") > 0 then
                PRINT #2, ""
                PRINT #2, "BANKISEL MACRO reg"

                PRINT #2, "  ;BANKISEL macro to restore missing Directive."
                ' alternate see https://www.microchip.com/forums/FindPost/1173118 post #39
                'print #2, "dw (0x1000 | ((high(reg) and 01h)<<10) |(7<<7)|STATUS) // BCF/BSF STATUS,IRP"
                PRINT #2, "  if reg < 0x100 "
                PRINT #2, "   ;MESSG "+CHR(34)+"Bankvalue < 256 - bcf"+CHR(34)
                PRINT #2, "   bcf STATUS, 7"
                PRINT #2, "  else"
                PRINT #2, "   ;MESSG "+CHR(34)+"Bankvalue > 256 - bsf"+CHR(34)
                PRINT #2, "   bsf STATUS, 7"
                PRINT #2, "  endif"
                PRINT #2, "ENDM"
              End if
            End If

          PRINT #2, ""
      End if


      'Publish CONFIG for PIC
      if AFISupport = 1 and ConfigDisabled =0 then
        Print #2, Star80
        Print #2, ";Explicit CONFIG"
      end if

    CurrLine = ChipConfigCode->CodeList->Next
    legacyConfigPublished = 0

    Do While CurrLine <> 0

      'Purge duplicate configs for .S only
      dim as Integer CD
      dim ConfName as string
      FOR CD = 1 TO DCOC
        ConfName = trim(Left(DefCONFIG(CD), INSTR(DefCONFIG(CD), "=") - 1))
        if INSTR( ucase(CurrLine->value), ucase(ConfName)) > 0 then
          'we have a match; so comment the DefCONFIG, we cannot delete as we need to restore the value later.
          DefCONFIG(CD) = ";"+DefCONFIG(CD)
        end if
      Next

      'Publish CONFIGs
      If len(CurrLine->Value) < 196 and AFISupport = 0 then
          'less than 196 and not PICAS, so, legacy config statement
          Print #1, CurrLine->Value
          CurrLine = CurrLine->Next

      else
          'so, PICAS mode but do not want to multiple line the config for legacy chips
          if len(CurrLine->Value) < 196 and AFISupport = 1 then
              legacyConfigPublished = -1
              Print #1, CurrLine->Value
          end if

          'export CONFIG  onto on line per entry to stop MPASMx from crashing!
          adaptedConfigLine = CurrLine->Value
          'transform config for non ChipFamily16, only non ChipFamily16 will have GetMetaData(Currline)->RawConfig
          If GetMetaData(Currline)->RawConfig <> "" then

            If instr( GetMetaData(Currline)->RawConfig, "&" ) > 0 then
              StringSplit ( GetMetaData(Currline)->RawConfig, "&",-1,configarray() )
            Else
             redim configarray(1)
             configarray(0) = GetMetaData(Currline)->RawConfig
            End if

            'transform old ASM CONFIG to NEW PIC-AS CONFIG
            for configarraycounter = 0 to ubound(configarray)

               configarray(configarraycounter ) = trim( configarray(configarraycounter )  )  'remove the spaces
               if configarray(configarraycounter ) <> "" then
                 configarray(configarraycounter ) = mid ( configarray(configarraycounter ) , 2 ) 'renove the leading underscore
                 replace( configarray(configarraycounter ), "_", " = " ) 'replace the underscore with =

                 if AFISupport = 1 then print #2,  ConfigTidy( configarray(configarraycounter ) )
               end if

            next
            erase configarray
            'output the ASM, cus we have to maintain the ASM file for reference
            if legacyConfigPublished = 0 then Print #1, CurrLine->Value
            goto ConfigDone
          End if

          'Print multiple lines to ASM and S
          if left(trim(adaptedConfigLine), 6) = "CONFIG" then
              'must be a long 18f config line
              replace ( adaptedConfigLine, "CONFIG ","")
              StringSplit ( adaptedConfigLine, ",",-1,configarray() )
              for configarraycounter = 0 to ubound(configarray)
                if legacyConfigPublished = 0 then print #1, " CONFIG "+configarray(configarraycounter )
                if AFISupport = 1 then print #2, ConfigTidy( configarray(configarraycounter ) )
              next
          else

              if legacyConfigPublished = 0 then Print #1, CurrLine->Value
              if AFISupport = 1 then Print #2, CurrLine->Value
          end if


          ConfigDone:
          CurrLine = CurrLine->Next
          legacyConfigPublished = 0

      End if

    Loop

    'Output the other default configs - these are used to the the HEX config, so, we need them for PIC-AS to create the same HEX
    if AFISupport = 1 and ConfigDisabled = 0 then
      Print #2, ";Inferred CONFIG"
      dim as Integer CD
      FOR CD = 1 TO DCOC
        if left(DefCONFIG(CD),1) <> ";" then
          ' print #2, " CONFIG  " +DefCONFIG(CD)
          print #2, ConfigTidy(DefCONFIG(CD))
        end if
      Next
      print #2, ""
    end if

    'We have to restore Duplicate configs.  They may have been commented out but we need them back for GCASM,
    'So, this becomes a non-destructive operations.
    dim as Integer CD
    FOR CD = 1 TO DCOC
      if left(DefCONFIG(CD),1) = ";" then
        'restore the initial value
        DefCONFIG(CD)=mid( DefCONFIG(CD),2)
      end if
    Next

    if AFISupport = 1 then

      PRINT #2, ""
      PRINT #2, Star80
      PRINT #2, "; The XC8 xc.inc include file.  This MUST be placed after the CONFIG statements."
      PRINT #2, ""
      PRINT #2, " #include <xc.inc>"
      PRINT #2, ""

    End if

  ElseIf ModeAVR Then
    Temp = LCase(ChipName)
    If INSTR(Temp, "usb") <> 0 Then
      Temp = MID(Temp, INSTR(Temp, "usb"))
    ElseIf INSTR(Temp, "can") <> 0 Then
      Temp = MID(Temp, INSTR(Temp, "can"))
    ElseIf Left(Temp, 4) = "mega" THEN
      Temp = "m" + Mid(Temp, 5)
    ElseIf Left(Temp, 4) = "tiny" THEN
      Temp = "tn" + Mid(Temp, 5)
    ElseIf INSTR(Temp, "s") <> 0 Then
      Temp = Mid(Temp, INSTR(Temp, "s") + 1)
    End If
    PRINT #1, ";Chip Model: " + ChipName
    PRINT #1, ";Assembler header file"
    If HashMapGet(Constants, "AVRASM2_INCLUDE_LOCATION" ) = 0  Then 
      PRINT #1, ".INCLUDE " + Chr(34) + Temp + "def.inc" + Chr(34)
    Else
      PRINT #1, ".INCLUDE " + Chr(34) + HashMapGetStr(Constants, "AVRASM2_INCLUDE_LOCATION") + Chr(34)
    End If
    Print #1, ""
    Print #1, ";SREG bit names (for AVR Assembler compatibility, GCBASIC uses different names)"
    VarList = HashMapToList(SysVarBits, -1)
    CurrLine = VarList->Next
    Do While CurrLine <> 0
      SysVar = CurrLine->MetaData
      If UCASE(SysVar->Parent) = "SREG" Then
        'Get letter of bit (C, Z, etc)
        BitName = Right(SysVar->Name, 1)
        If InStr(AddedBits, BitName) = 0 Then
          Print #1, "#define " + BitName + " " + Str(SysVar->Location)
        End If
        AddedBits += BitName
      End If
      CurrLine = CurrLine->Next
    Loop
    LinkedListDelete(VarList, 0)

  End If
  PRINT #1, ""

  'Out All constants
  If CDFSupport = 1 Then
    Print #CDFFileHandle, ""
    'use FinalConstantList
    Dim as String templine, explandedtempline 
    FinalConstantList = HashMapToList(Constants, -1)
    IF FinalConstantList <> 0 AndAlso FinalConstantList->Next <> 0 THEN
      CurrLine = FinalConstantList->Next
      Do While CurrLine <> 0
        templine = CurrLine->value
        explandedtempline = templine
        explandedtempline = ReplaceConstantsLine(explandedtempline,0)
        If InStr(UCase(explandedtempline), ";STRING") <> 0 Then
          templine = GetString(explandedtempline)
           Print #CDFFileHandle  ,Space(14) + "FINAL/CONSTANT :" +  Left( CurrLine->value+Space(40),40)  ,  chr(34)+templine+chr(34), ReplaceConstantsLine(explandedtempline,0)
        else
           Print #CDFFileHandle  ,Space(14) + "FINAL/CONSTANT :" +  Left(tempLine+Space(40),40)  ,  ReplaceConstantsLine(templine,0)
        end if
       
        CurrLine = CurrLine->Next
      Loop
    End If
  End If

  VarList = HashMapToList(FinalVarList, -1)
  IF VarList <> 0 AndAlso VarList->Next <> 0 THEN
    Dim localoutline as string

    PRINT #1, Star80
    PRINT #1, ""
    PRINT #1, ";Set aside memory locations for variables"
    If ModePIC Then PRINT #1, ";  Shared/Access RAM = (SA)"

    If AFISupport = 1 then
      PRINT #2, Star80
      PRINT #2, ""
      PRINT #2, ";Set aside RAM memory locations for variables. All variables are global."
    End if

    CurrLine = VarList->Next
    Do While CurrLine <> 0
      With *CPtr(VariableListElement Pointer, CurrLine->MetaData)
        If ModePIC and AFISupport = 1 then
          'PICAS
          If IsInAccessBank(.Name) Then
            PICASVarDecLine = " "+left(.Name+Pad32, 32) + " EQU " + trim(.Value) +"          ; 0x"+ Hex(val(.Value)) + " (SA)"
            'ASM
            VarDecLine = left(.Name+Pad32, 32) + " EQU " + right( Pad32+trim(.Value),7) +"          ; 0x"+ Hex(val(.Value)) + " (SA)"
          Else
            PICASVarDecLine = " "+left(.Name+Pad32, 32) + " EQU " + trim(.Value) +"          ; 0x"+ Hex(val(.Value))
            'ASM
            VarDecLine = left(.Name+Pad32, 32) + " EQU " + right( Pad32+trim(.Value),7) +"          ; 0x"+ Hex(val(.Value))
          End IF
          localoutline = "Global "+.Name

        ElseIf ModePIC Or ModeZ8 Then
          If IsInAccessBank(.Name) Then
            VarDecLine = left(.Name+Pad32, 32) + " EQU " + right( Pad32+trim(.Value),7) +"          ; 0x"+ Hex(val(.Value)) + " (SA)"
          Else
            VarDecLine = left(.Name+Pad32, 32) + " EQU " + right( Pad32+trim(.Value),7) +"          ; 0x"+ Hex(val(.Value))
          End If
        ElseIf ModeAVR Then
          VarDecLine = ".EQU " + .Name + "=" + trim(.Value) +"          ; 0x"+ Hex(val(.Value))
        Else
          VarDecLine = ";Write variable declaring code!"
        End If
      End With
      PRINT #1, AsmTidy(VarDecLine, -1 )
      if AFISupport = 1 then
        if PreserveMode <> 0 then PRINT #2, AsmTidy( localoutline, 0 )
        PRINT #2, AsmTidy(PICASVarDecLine, 0 )
      End if
      CurrLine = CurrLine->Next
    Loop
    PRINT #1, ""
  END IF
  PRINT #1, Star80
  PRINT #1, ""
  if AFISupport = 1 then
      PRINT #2, ""
      PRINT #2, Star80
      PRINT #2, ""
  End if


  'Registers
  If ModeAVR Then
    Print #1, AsmTidy(";Register variables", -1 )
    FOR PD = 1 TO FRLC
      With FinalRegList(PD)
        If ModePIC Or ModeZ8 Then
          VarDecLine = .Name + " EQU " + .Value
        ElseIf ModeAVR Then
          VarDecLine = ".DEF " + .Name + "=" + .Value
        Else
          VarDecLine = ";Write variable declaring code!"
        End If
      End With
      PRINT #1, AsmTidy(VarDecLine, -1 )
    NEXT
    PRINT #1, ""
    PRINT #1, Star80
    PRINT #1, ""

  End If

  'Aliases
  IF FALC > 0 THEN
    PRINT #1, AsmTidy(";Alias variables", -1 )
    PRINT #2, AsmTidy(";Alias variables", 0 )

    Dim localoutline as string

    FOR PD = 1 TO FALC
      With FinalAliasList(PD)

       If ModePIC and AFISupport = 1 then
          'PICAS
          PICASVarDecLine = " "+left(.Name+Pad32, 32) + " EQU " + trim(.Value)
          localoutline = "Global "+.Name
          'ASM
          VarDecLine = left(.Name+Pad32, 32) + " EQU " + .Value
        ElseIf ModePIC Or ModeZ8 Then
          VarDecLine = .Name + " EQU " + .Value
        ElseIf ModeAVR Then
          VarDecLine = "#define " + .Name + " " + .Value
        Else
          VarDecLine = ";Write variable declaring code!"
        End If
      End With
      PRINT #1, AsmTidy(VarDecLine, -1 )
      if AFISupport = 1 then
        if PreserveMode <> 0 then PRINT #2, AsmTidy( localoutline, 0 )
        PRINT #2, AsmTidy(PICASVarDecLine, 0 )
      End if
    NEXT
    PRINT #1, ""
    PRINT #1, Star80
    PRINT #1, ""
    PRINT #2, ""
    PRINT #2, Star80
    PRINT #2,
  END IF


  If ModePIC Then
      'Add the  Initial vector for PICAS
      if AFISupport = 1 then
        ' PIC10/12/16 devices use a delta of 2 and PIC18 devices use a delta of 1.
        Select Case ChipFamily
          Case 16
            PRINT #2, " PSECT   RESETVEC,delta=1, abs"
          Case Else
            PRINT #2, " PSECT   PROGMEM0,delta=2, abs"
        End Select
        PRINT #2, " RESETVEC:"
      end if
  END IF

  'Program code
  CurrLine = CompilerOutput->CodeList->Next
  if AFISupport = 1 then
    Print message ("PICASMStartDotS")
  end if

  Do While CurrLine <> 0

    IF GetMetaData(Currline)->IsPICAS = -1 then
     'This is specific line for PICAS
     'Implementation assumes only PSECT PAGE COMMANDS - not required for PIC18
     If ChipFamily <> 16 then
        If AFISupport = 1 then
          PRINT #2, AsmTidy(CurrLine->Value, -1 )
        Else
          'Comment out line as GCASM will barf, if, still in list.
          CurrLine->Value = ";"+CurrLine->Value
        End if
     End if
     CurrLine = CurrLine->Next
     Continue Do

    end if

    'If REPROCES line then do not geneate / process for ASM
    IF instr( UCase(CurrLine->Value), "REPROCES") <> 0 then
      CurrLine = CurrLine->Next
      Continue Do
    End If

    IF instr( UCase(CurrLine->Value), "ALIGN 2") = 0 then
      PRINT #1, AsmTidy(CurrLine->Value, -1 )
    end if

    'Output and transform ASM to PICAS code
    'This essentially transforms to ensure all registers are correct, and all register.bit(s) are numeric constants

    If AFISupport = 1 then

      If PICASDEBUG AND NOT PICASDEBUGmessageShown Then 
        Print "PICASDEBUG enabled - see .S output file"
        PICASDEBUGmessageShown = True  'this ensure the message is shown only once.
      End If

      if left(CurrLine->Value,1) <> ";" then

          'the now implies just PIC 
          'If NOT a label, adapt the register and the register.bit to ensure compilation by PIC-AS

          'Define vars and init vars
          dim outline as string
          
          dim ASMInstruction as string
          dim Param1 as string
          dim Param2 as string
          dim Param3 as string
          dim elements0() as string
          dim outstring as string
          dim preservedline as Integer
          dim showonceflag as Byte = 0
          outline = CurrLine->Value
          preservedline = 0

          if GetMetaData(Currline)->IsLabel = 0 then
            
            Select Case ChipFamily

            Case 16 'therefore 18Fs
                  'Some #RAWASM is actually hidden in PRESERVE lines, so, we need to extract and tranform
                  If Left(outline, 8) = "PRESERVE" Then
                      preservedline = -1
                      Dim as Integer PresPos
                      PresPos = VAL(Mid(outline, 10))                    
                      outline  = PreserveCode(PresPos)
                  End if

                  'This section reverse lookups into the PICAS INC file in there is comma....
                  If instr( outline, "," ) > 0 then

                      'Take the initial line of code and split into the array - so BCF OSCCON1,NDIV3,BANKED becomes 3 elements (BCF OSCCON1),(NDIV3),(BANKED)
                      ' but MOVFF TMRVALUE,TMR2 is....
                      ' but BCF OSCCON1,NDIV3,BANKED
                      erase currentLineElements

                      ' count the number of commas'.  This will identify the format of the outline
                      dim as integer charCount = 0
                      dim i as integer
                      for i = 0 to len(outline)-1
                          if outline[i] = ASC(",") then
                            charCount = charCount + 1
                          end if
                      next 

                      Select Case charCount 
                        Case 1
                          ' example "bsf      NVMCON1,WR"  | "MOVWF	OSCCON1,BANKED"
                          ' MOVFF TMRVALUE,TMR2
                          replaceall ( OutLine, "  ", " " )

                          StringSplit ( trim(outline), ",",-1,currentLineElements() )
                          Param3 = currentLineElements(1)

                          erase elements0
                          StringSplit( trim(currentLineElements(0)), " " ,3, elements0() )

                          ASMInstruction = " "+elements0(0)
                          Param2 = elements0(1)

                        Case 2

                          StringSplit ( trim(outline), ",",-1,currentLineElements() )
                          Param2 = currentLineElements(1)

                          Param3 = ""
                          if ubound(currentLineElements) > 1  then
                            Param3 = trim(currentLineElements(2))
                          end if
                      
                          'So, (BCF OSCCON1) becomes ((BCF) (OSCCON1)
                          erase elements0
                          StringSplit( trim(currentLineElements(0)), " " ,3, elements0() )

                          ASMInstruction = " "+elements0(0)
                          Param1 = elements0(1)

                        Case Else
                          ' items that are not processed
                          If Left(outline,1) <> ";" and Left(trim(outline),2) <> "DB" and Left(trim(outline),2) <> "DW" Then
                            LogWarning "GCBASIC compiler: PIC-AS conversion unhandled for "+outline
                          End If
                      End Select

                      'First test: Does the SECOND element exist as SYSVAR? and therefore a register   like  BCF 'OSCCON1',NDIV3,BANKED
                      if GetSysVar(Param1) <> 0 then
                        outstring = GetReversePICASIncFileLookupValue ( GetSysVar(Param1)->location )
                        if outstring <> "" then
                    
                              replace ( outline , trim(Param1) , outstring )
                              outline = outline
                              if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                if PICASDEBUG then Print #2, ";A2: ASM Source was: "+CurrLine->Value + "  Param1 = " + trim(Param1) + " - target = "+outstring + " now " + outline
                                CurrLine->Value = outline
                              end if

                              'assign here so we can see the debug
                              Param1 = outstring

                              'We know that we are dealing with a register, so, reverse lookup was correct
                              if Param3 = "" then
                                outline = ASMInstruction+" "+outstring+","+Param2
                                if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                  if PICASDEBUG then Print #2, ";A4: ASM Source was: "+CurrLine->Value
                                end if
                              else
                                outline = ASMInstruction+" "+outstring+","+Param2+","+Param3
                                if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                  if PICASDEBUG then Print #2, ";A5: ASM Source was: "+CurrLine->Value + " target = " + outline
                                end if

                              end if

                        else
                              replace ( outline , trim(Param1) , ucase( trim(Param1) ) )
                              outline = outline
                              if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                if PICASDEBUG then Print #2, ";A3: ASM Source was: "+CurrLine->Value
                              end if
                        end if

                      else

                          if GetSysVar(Trim(Param2)) <> 0 then
                              outstring = GetReversePICASIncFileLookupValue ( GetSysVar(Trim(Param2))->location )
                              if outstring <> "" then
                                  If Instr(Ucase(Param3), "BANKED") <> 0 or Instr(Ucase(Param3), "ACCESS") <> 0  Then                                 
                                    outline = ASMInstruction+" "+OutString+","+Param3
                                    if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                      if PICASDEBUG then Print #2, ";A6a: ASM Source was: "+CurrLine->Value
                                    end if
                                  Else
                                    'Test if Param3 is a Bit, if is convert, else, it is register.
                                    if trim(GetSFRBitValue(trim(Param3))) <> "" then
                                      outline = ASMInstruction+" "+Param2 + "," + GetSFRBitValue(trim(Param3))
                                    else
                                      outline = ASMInstruction+" "+Param2 + "," + trim(Param3)
                                    end if
                                    if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                      if PICASDEBUG then Print #2, ";A6b: ASM Source was: "+CurrLine->Value
                                    end if
                                  End if

                              end if
                          end if

                          if GetSysVar(trim(Param3)) <> 0 then
                              outstring = GetReversePICASIncFileLookupValue ( GetSysVar(Trim(Param3))->location )
                         


                              if outstring <> "" then
                                  If Instr(Ucase(Param3), "BANKED") <> 0 or Instr(Ucase(Param3), "ACCESS") <> 0  Then                                 
                                    outline = ASMInstruction+" "+Param2 + ","+OutString
                                    if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                      if PICASDEBUG then Print #2, ";A6c: ASM Source was: "+CurrLine->Value
                                    end if
                                  Else
                                    'Test if Param2 is a Bit, if is convert, else, it is register.
                                    if trim(GetSFRBitValue(trim(Param2))) <> "" then
                                      outline = ASMInstruction+" "+GetSFRBitValue(trim(Param2)) + "," + trim(outstring)
                                    else
                                      outline = ASMInstruction+" "+Param2 + "," + trim(outstring)
                                    end if
                                    if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                      if PICASDEBUG then Print #2, ";A6d: ASM Source was: "+CurrLine->Value
                                    end if
                                  End if

                              end if
                          end if

                      end if

                      'Second test for register.bit  transforms the BITs to a constant to resolve the reverse lookup
                      if HasSFRBit ( Param2 )  then
                          Param2 = GetSFRBitValue(trim( Param2 ))
                          if Param3 = "" then
                            outline = ASMInstruction+" "+Param1+","+Param2
                            if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                              if PICASDEBUG then Print #2, ";A7: ASM Source was: "+CurrLine->Value
                            end if
                          else
                            outline = ASMInstruction+" "+Param1+","+Param2+","+Param3
                            if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                if PICASDEBUG then Print #2, ";A8: ASM Source was: "+CurrLine->Value   + " target = " + outline
                            end if
                          end if
                      end if

                  else
                      'lots of code looks like this... "BANKSEL ADCON0"  where there is register.. check the register is valid and reverse
                      erase currentLineElements

                      'Inspect for a space - threfore, must be more that one paramater
                      if instr(trim(outline), " ") <> 0 and left(trim(outline),1) <> ";"  then
                        Dim tmpOutLine as String = outline
                        replaceall ( tmpOutLine, "(", " " )
                        replaceall ( tmpOutLine, ")", " " )
                        replaceall ( tmpOutLine, "[", " " )
                        replaceall ( tmpOutLine, "]", " " )
                        replaceall ( tmpOutLine, ">>", " " )
                        replaceall ( tmpOutLine, "<<", " " )
                        replaceall ( tmpOutLine, "  ", " " )
                        
                        StringSplit ( trim(tmpOutLine), " ",-1,currentLineElements() )                          
                        If UserCodeOnlyEnabled = -1 then
                          Dim elementcounter as Byte
                          Dim registerbitlocation as String
                          'walk the elements replacing BIT constant will value using the reverse lookups
                          For elementcounter = 0 to ubound(currentLineElements)
                          
                              if GetSFRBitValue(trim(currentLineElements(elementcounter))) <> "" then
                                  registerbitlocation = GetSFRBitValue(trim(currentLineElements(elementcounter)))
                                  
                                  if registerbitlocation <> "" then           
                            
                                      If showonceflag = 0 and Instr(Ucase(outline), "#ASMRAW") = 0  and  Left(outline, 8) <> "PRESERVE" and trim(ucase(registerbitlocation)) <> trim(ucase(currentLineElements(elementcounter))) then
                                        Print #2, ";"+trim(outline)
                                        showonceflag = -1
                                      end if

                                      replace( outline, currentLineElements(elementcounter), registerbitlocation )

                                  end if

                              end if

                          Next
                        else
                          StringSplit ( trim(outline), " ",-1,currentLineElements() )
                          Param1 = currentLineElements(0)
                          Param2 = currentLineElements(1)

                          if GetSysVar(Param2) <> 0 then
                            outstring = GetReversePICASIncFileLookupValue ( GetSysVar(Param2)->location )

                            if outstring <> "" then
                                  outline = Param1+" "+outstring
                                  if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                    if PICASDEBUG then Print #2, ";A9: ASM Source was: "+CurrLine->Value
                                  end if
                            end if
                          end if
                        end if
                      end if
                  end if


            Case Else  '16F etc
                  If instr( outline, "," ) > 0 then
                      'Assumes that any ASM that has a command requires analysis and transformation for PIC-AS

                      'Take the initial line of code and split into the array into the array currentLineElements()
                      erase currentLineElements
                      StringSplit ( trim(outline), ",",-1,currentLineElements() )
                      Param2 = currentLineElements(1)

                      Param3 = ""
                      if ubound(currentLineElements) > 1  then
                        Param3 = trim(currentLineElements(2))
                      end if

                      'So, split the first element into elements0()
                      erase elements0
                      StringSplit( trim(currentLineElements(0)), " " ,3, elements0() )
                      ASMInstruction = " "+elements0(0)
                      Param1 = elements0(1)

                      'print outline, "=",ASMInstruction+" "+Param1+","+Param2+","+Param3+"           ;B1"

                      'First test: Does the SECOND element exist as getSYSVAR and therefore a register   like  BCF 'OSCCON1',NDIV3,BANKED
                      if GetSysVar(Param1) <> 0 then
                        outstring = GetReversePICASIncFileLookupValue ( GetSysVar(Param1)->location )
                        if outstring <> "" then

                              Param1 = outstring
                              outline = ASMInstruction+" "+Param1+","+Param2
                              if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                if PICASDEBUG then Print #2, ";B2: ASM Source was: "+CurrLine->Value
                              end if

                        else
                              replace ( outline , trim(Param1) , ucase( trim(Param1) ) )
                              outline = outline
                              if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                if PICASDEBUG then Print #2, ";B3: ASM Source was: "+CurrLine->Value
                              end if
                        end if

                        'We know that we are dealing with a register, so, reverse lookup to correct
                        outstring = GetReversePICASIncFileLookupValue ( GetSysVar(Param1)->location )
                        if outstring <> "" then
                          Param1 = outstring
                          if Param3 = "" then
                            outline = ASMInstruction+" "+Param1+","+Param2
                            if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                              if PICASDEBUG then Print #2, ";B4: ASM Source was: "+CurrLine->Value
                            end if
                          else
                            outline = ASMInstruction+" "+Param1+","+Param2+","+Param3
                            if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                              if PICASDEBUG then Print #2, ";B5: ASM Source was: "+CurrLine->Value
                            end if

                          end if
                        end if

                      else

                          if GetSysVar(Param2) <> 0 then
                              outstring = GetReversePICASIncFileLookupValue ( GetSysVar(Param2)->location )
                              if outstring <> "" then
                                  Param2 = outstring
                                  outline = chr(9)+ASMInstruction+" "+Param1+","+Param2
                                  if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                    if PICASDEBUG then Print #2, ";B6: ASM Source was: "+CurrLine->Value
                                  end if

                              end if
                          end if

                      end if

                      'Second test for register.bit  transforms the BITs to a constant to resolve the reverse lookup
                      'Section can be improved to replace the constant with he targtet bit - tested on 16/18f
                      if HasSFRBit ( Param2 )  then
                          'replace this with an explict look up into the .h file 
                          'meanwhile use a constant
                          Param2 = GetSFRBitValue(trim( Param2 ))
                          if Param3 = "" then
                            outline = ASMInstruction+" "+Param1+","+Param2
                            if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                              if PICASDEBUG then Print #2, ";B7: ASM Source was: "+CurrLine->Value
                            end if
                          else
                            outline = chr(9)+ASMInstruction+" "+Param1+","+Param2+","+Param3
                            if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                if PICASDEBUG then Print #2, ";B8: ASM Source was: "+CurrLine->Value
                            end if
                          end if
                      end if

                  else                  
                      'lots of code looks like this... "BANKSEL ADCON0"  where there is register.. check the register is valid and reverse
                      'code could be ( 1 << BIT ) or ( 1 << WR ) etc
                      erase currentLineElements

                      'Some #RAWASM is actually hidden in PRESERVE lines, so, we need to extract and tranform
                      If Left(outline, 8) = "PRESERVE" Then
                          preservedline = -1
                          Dim as Integer PresPos
                          PresPos = VAL(Mid(outline, 10))
                          outline = PreserveCode(PresPos)
                      End if
                                          
                      if instr(trim(outline), " ") <> 0 then
                          Dim tmpOutLine as String = outline
                          replaceall ( tmpOutLine, "(", " " )
                          replaceall ( tmpOutLine, ")", " " )
                          replaceall ( tmpOutLine, "[", " " )
                          replaceall ( tmpOutLine, "]", " " )
                          replaceall ( tmpOutLine, ">>", " " )
                          replaceall ( tmpOutLine, "<<", " " )
                          replaceall ( tmpOutLine, "  ", " " )
                          
                          StringSplit ( trim(tmpOutLine), " ",-1,currentLineElements() )
                          If UserCodeOnlyEnabled = -1 then
                            Dim elementcounter as Byte
                            Dim registerbitlocation as String
                            'walk the elements replacing BIT constant will value using the reverse lookups
                            For elementcounter = 0 to ubound(currentLineElements)
                            
                                if GetSFRBitValue(trim(currentLineElements(elementcounter))) <> "" then
                                    registerbitlocation = GetSFRBitValue(trim(currentLineElements(elementcounter)))
                                    
                                    if registerbitlocation <> "" then           
                              
                                        If showonceflag = 0 and Instr(Ucase(outline), "#ASMRAW") = 0  and  Left(outline, 8) <> "PRESERVE" and trim(ucase(registerbitlocation)) <> trim(ucase(currentLineElements(elementcounter))) then
                                          Print #2, ";"+trim(outline)
                                          showonceflag = -1
                                        end if

                                        replace( outline, currentLineElements(elementcounter), registerbitlocation )

                                    end if

                                end if

                            Next

                          else
                            ' reverse lookup Param2. Things like BANKSEL NVMCON1. This transformation is to ensure the register is the same name
                            Param1 = currentLineElements(0)
                            Param2 = currentLineElements(1)

                            if GetSysVar(Param2) <> 0 then
                              Param2 = GetReversePICASIncFileLookupValue ( GetSysVar(Param2)->location )

                              if Param2 <> "" then
                                    outline = chr(9)+Param1+" "+Param2
                                    if trim(CurrLine->Value) <> trim(outline)  and PreserveMode = 2 then
                                      if PICASDEBUG then Print #2, ";B9: ASM Source was: "+CurrLine->Value
                                    end if
                              end if
                            end if
                          end if
                          
                      end if

                  end if
            End Select

          else
            'must be a label

            if trim(outline) <> "" then
                if PreserveMode <> 0 then print #2,  AsmTidy( "Global " +ucase(outline) ,0 )
                outline= outline+":"
            End if


          end if

          'pad the tab
          if ucase(left(trim(outline),7)) =  "BANKSEL" then
              outline = chr(9)+trim(outline)
          End if

          print #2, AsmTidy( outline, 0 )
      else
        print #2, AsmTidy( CurrLine->Value, 0 )
      end if

    end if


    CurrLine = CurrLine->Next
  Loop
  PRINT #1, ""
  If ModePIC Then
      if AFISupport = 1 then
        PRINT #2, ";"
        PRINT #2, "; Declare Power-On-Reset entry point"
        PRINT #2, ";"
        PRINT #2, " END     RESETVEC"
        CLOSE #2

        'reopen for 2.35 fix - as the comments are corrupted
        'removed constraint check but this probable means that 2.32 and 2.35 will fail to compile.. use a version of PIC-AS from 2.4x or greater
        'If Instr(Ucase(ChipPICASRoot),"2.32") > 0 or Instr(Ucase(ChipPICASRoot),"2.35") > 0 then
            OPEN AFI FOR INPUT AS #2
            OPEN AFI+".tmp" FOR OUTPUT AS #3
            Dim DataSource as String
            Do While NOT EOF(2)
                Line Input #2,  DataSource
                If left( DataSource, 1 )=";" Then
                  If left( DataSource, 2 )=";"+Chr(34)  Then
                    Print #3, DataSource
                  ElseIf left( DataSource, 2 )=";'"  Then
                    Print #3, DataSource
                  Else
                    'muset be just the ;
                    DataSource = ";'"+mid( DataSource,2 )
                    Print #3, DataSource
                  End If
                Else
                    Print #3, DataSource
                End if
            Loop
            Close #2
            Close #3
            FileCopy ( AFI+".tmp", AFI )
            Kill ( AFI+".tmp")
        'End If



        PRINT #1, " END"

      else
        PRINT #1, " END"
      end if
  End if
  CLOSE #1

  if AFISupport = 1 then
    Print message ("PICASMWriteDotS")
  end if

  
  End Sub

Sub WriteCompilationReport
  'Create a report on compilation process

  Dim As String RF, ReportFileName, OutData, Temp, CallText
  Dim As Integer F, CurrSubNo, CurrBank, CurrBankLoc
  Dim As SubType Pointer CurrSub, CalledSub
  Dim As LinkedListElement Pointer ListItem

  If nonAsciiFound = -1 then Exit Sub

  'Save typing
  RF = CompReportFormat

  'If format starts with "s_", then skip report if no compilation done
  If Left(RF, 2) = "s_" Then
    If CompileSkipped Then
      Exit Sub
    End If
    'Not skipping, remove s_ and continue
    RF = Mid(RF, 3)
  End If

  'Check requested format: valid options are HTML or plain text
  If RF <> "html" And RF <> "text" Then Exit Sub

  'Open file for output
  If RF = "html" Then
    ReportFileName = ReplaceToolVariables("%filename%", "html")
  ElseIf RF = "text" Then
    ReportFileName = ReplaceToolVariables("%filename%", "report.txt")
  End If
  F = FreeFile
  Open ReportFileName For Output As #F

  'Write header of file
  If RF = "html" Then
    Print #F, "<!DOCTYPE html>"
    Print #F, "<html>"
    Print #F, "<head>"
    Print #F, "<title>" + Message("CRTitle") + "</title>"
    Print #F, "</head>"
    Print #F, "<body>"
    Print #F, "<h1>" + Message("CRTitle") + "</h1>"
  ElseIf RF = "text" Then
    Print #F, Message("CRTitle")
    Print #F, ""
  End If

  'Format the version string
  If Right(Trim(Version),1) <> ")" Then Version = Version + ")"

  'Write compiler information
  If RF = "html" Then

    If MuteDonate = 0 Then

        Randomize timer
        Select Case  int(Rnd * (10 - 1) + 1)
          Case 1:
        Print #F, "<p>" + Message("CRVersion") + ": " + Version + "</p>"+Message("PromoMessage1")

          Case 2:
        Print #F, "<p>" + Message("CRVersion") + ": " + Version + "</p>"+Message("PromoMessage2")

          Case 3:
        Print #F, "<p>" + Message("CRVersion") + ": " + Version + "</p>"+Message("PromoMessage3")

          Case Else
        Print #F, "<p>" + Message("CRVersion") + ": " + Version + "</p>"

        End Select
    Else
      Print #F, "<p>" + Message("CRVersion") + ": " + Version + "</p>"
    End If

  ElseIf RF = "text" Then
    Print #F, Message("CRVersion") + ": " + Version
    Print #F, ""
    Print #F, Star80
    Print #F, ""
  End If

  'Write compilation time and messages produced
  If RF = "html" Then
    Print #F, "<h2>" + Message("CRCompTime") + "</h2>"
    Print #F, "<p>" + Message("CompTime") + Str(CompEndTime - StartTime) + Message("CompSecs") + "</p>"
    Print #F, "<p>" + Message("AsmTime") + Str(AsmEndTime - CompEndTime) + Message("CompSecs") + "</p>"
    Print #F, "<p>" + Message("ProgTime") + Str(ProgEndTime - AsmEndTime) + Message("CompSecs") + "</p>"
    Print #F, "<p>" + Message("TotalTime") + Str(ProgEndTime - StartTime) + Message("CompSecs") + "</p>"
  ElseIf RF = "text" Then
    Print #F, Message("CRCompTime") + ":"
    Print #F, Message("CompTime") + Str(CompEndTime - StartTime) + Message("CompSecs")
    Print #F, Message("AsmTime") + Str(AsmEndTime - CompEndTime) + Message("CompSecs")
    Print #F, Message("ProgTime") + Str(ProgEndTime - AsmEndTime) + Message("CompSecs")
    Print #F, Message("TotalTime") + Str(ProgEndTime - StartTime) + Message("CompSecs")
    Print #F, ""
    Print #F, Star80
    Print #F, ""
  End If

  'Write chip resource usage information
  Dim As String UsedProgram, UsedRAM, OscType
  UsedProgram = Message("UsedProgram")
  Replace UsedProgram, "%used%", Str(StatsUsedProgram + ReserveHighProg)
  Replace UsedProgram, "%total%", Str(ChipProg)
  If ChipProg <> 0 Then UsedProgram += Format((StatsUsedProgram + ReserveHighProg) / ChipProg, " (###.##%)")
  UsedRAM = Message("UsedRAM")
  Replace UsedRAM, "%used%", Str(StatsUsedRam+RegBytesUsed)
  Replace UsedRAM, "%total%", Str(ChipRAM)
  If ChipRAM <> 0 Then UsedRAM += Format(StatsUsedRAM / ChipRAM, " (###.##%)")

  OscType = ""
  If ModePIC Then
    If HashMapGet(Constants, "CHIPUSINGINTOSC") <> 0 Then
      OscType = " (" + Message("CRIntOsc") + ")"
    Else
      OscType = " (" + Message("CRExtOsc") + ")"
    End If
  End If

  If RF = "html" Then
    Print #F, "<h2>" + Message("ChipUsage") + "</h2>"
    'This needs to match g@Stools.bas
    Print #F, "<p>" + Message("ChipM") + ChipName + "</p>"
    Print #F, "<p>" + UsedProgram + "</p>"
    Print #F, "<p>" + UsedRAM + "</p>"
    If ModePIC Then
        Print #F, "<p> OSC: " + ChipOscSource + ", " + Str(ChipMhz) + "Mhz" + OscType + "</p>"
    Else
        Print #F, "<p> OSC: " + Str(ChipMhz) + "Mhz</p>"
    End if

  ElseIf RF = "text" Then
    Print #F, Message("ChipUsage")
    Print #F, Message("ChipM") + ChipName
    Print #F, UsedProgram
    Print #F, UsedRAM
    Print #F, ChipOscSource + OscType
    Print #F, ""
    Print #F, Star80
    Print #F, ""
  End If

  'Write chip pin usage information

  'Write variable sizes/locations
  'Display variable name, type and location
  If RF = "html" Then
    Print #F, "<h2>" + Message("CRRAM") + "</h2>"
    Print #F, "<table border=""1"">"
  Else
    Print #F, Message("CRRAM")
  End If

  If RF = "html" Then
    Print #F, "</table>"
  ElseIf RF = "text" Then
    Print #F, Star80
    Print #F, ""
  End If

  'Write subroutine sizes/locations/calls
  If RF = "html" Then
    Print #F, "<h2>" + Message("CRSubInfo") + "</h2>"
    Print #F, "<table border=""1"">"
    OutData = "<tr><th>" + Message("CRName") + "</th><th>" + Message("CROriginalSize")
    OutData = OutData + "</th><th>" + Message("CRSize") + "</th><th>"
    If ModePIC And (ProgMemPages > 1) Then
      OutData = OutData + Message("CRPage") + "</th><th>"
    End If
    OutData = OutData + Message("CRCalls") + "</th></tr>"
    Print #F, OutData

  ElseIf RF = "text" Then
    Print #F, Message("CRSubInfo")
  End If
  For CurrSubNo = 0 To SBC
    CurrSub = Subroutine(CurrSubNo)

    If CurrSub->Required Then
      'Get call list
      CallText = ""
      ListItem = CurrSub->CallList->Next
      Do While ListItem <> 0
        CalledSub = ListItem->Metadata
        If CallText = "" Then
          CallText = CalledSub->Name + "(" + Str(ListItem->NumVal) + ")"
        Else
          CallText = CallText + ", " + CalledSub->Name + "(" + Str(ListItem->NumVal) + ")"
        End If
        ListItem = ListItem->Next
      Loop

        'Write information
      If RF = "html" Then
        OutData = "<tr><td>" + CurrSub->Name + "</td>"
        OutData += "<td>" + Str(CurrSub->OriginalLOC) + "</td>"
        OutData += "<td>" + Str(CurrSub->HexSize) + "</td>"
        If ModePIC And (ProgMemPages > 1) Then
          OutData += "<td>" + Str(CurrSub->DestPage) + "</td>"
        End If
        OutData += "<td>" + CallText + "</td></tr>"
        Print #F, OutData

      ElseIf RF = "text" Then
        Print #F, CurrSub->Name
        Print #F, Message("CROriginalSize") + ": " + Str(CurrSub->OriginalLOC)
        Print #F, Message("CRSize") + ": " + Str(CurrSub->HexSize)
        If ModePIC And (ProgMemPages > 1) Then
          Print #F, Message("CRPage") + ": " + Str(CurrSub->DestPage)
        End If
        If CallText <> "" Then Print #F, Message("CRCalls") + ": " + CallText
        Print #F, ""
      End If
    End If
  Next

  'Finish
  If RF = "html" Then
    Print #F, "</table>"
    Print #F, "</body>"
    Print #F, "</html>"
  ElseIf RF = "text" Then
    Print #F, Star80
    Print #F, ""
  End If
  Close #F
  
  End Sub

Sub WriteErrorLog
  Dim As String Temp, MessageType, NewErr, ErrorFileName
  Dim As Integer CD, CS, PD, F1, L1, F2, L2, F, L, S, I, T
  Dim As Integer ERC, WRC, SortEnd

  'Set error log filename
  ErrorFileName = "Errors.txt"

  'Exit Sub if no errors are present
  IF OutMessages = 0 Then
    Kill ErrorFileName
    EXIT Sub
  End If

  'Get name of hex file to delete
  HFI = OFI
  For PD = Len(HFI) To 1 Step -1
    If Mid(HFI, PD, 1) = "." Then
      HFI = Left(HFI, PD) + "hex"
      Exit For
    End If
  Next
  IF Dir(HFI) <> "" THEN KILL HFI


  'Sort error list
  'Print "Sort error list"
  SortEnd = OutMessages - 1
  If OutMessages = MAX_OUTPUT_MESSAGES Then SortEnd = OutMessages - 2
  Do
    CS = 0
    FOR PD = 1 TO SortEnd
      'Get file and line of two errors
      F1 = 0: L1 = 0: F2 = 0: L2 = 0
      IF INSTR(OutMessage(PD), ";?F") <> 0 THEN
        Temp = OutMessage(PD)
        F1 = VAL(Mid(Temp, INSTR(Temp, "F")+1))
        L1 = VAL(Mid(Temp, InStr(Temp, "L")+1))
      END IF
      IF INSTR(OutMessage(PD + 1), ";?F") <> 0 THEN
        Temp = OutMessage(PD + 1)
        F2 = VAL(Mid(Temp, INSTR(Temp, "F")+1))
        L2 = VAL(Mid(Temp, INSTR(Temp, "L")+1))
      END IF

      'Compare, swap if necessary
      IF (F1 > F2) Or (F1 = F2 AND L1 > L2) THEN
        Swap OutMessage(PD), OutMessage(PD + 1)
        CS = 1
      END IF
    NEXT
  Loop While CS = 1
  'Print "Finished sorting"

  'Generate file:line references
  FOR PD = 1 to OutMessages
    'Get type
    MessageType = Right(OutMessage(PD), 1)
    OutMessage(PD) = Left(OutMessage(PD), LEN(OutMessage(PD)) - 1)
    If MessageType = "W" Then MessageType = Message("TypeWarning"): WRC += 1
    If MessageType = "E" Then MessageType = Message("TypeError"): ERC += 1
    If LEN(MessageType) > 1 And UCase(LEFT(OutMessage(PD), 4)) <> "ASM:" Then
      MessageType = MessageType + ": "
    Else
      MessageType = ""
    End If

    Dim As String Me, Fi
    IF INSTR(OutMessage(PD), ";?F") <> 0 THEN
      Temp = OutMessage(PD)
      'Get values
      Replace Temp, "?", ""
      Me = Trim(Mid(Temp, INSTR(Temp, "?") + 1)) 'Message
      F = VAL(Mid(Temp, INSTR(Temp, "F") + 1)) 'File
      L = VAL(Mid(Temp, INSTR(Temp, "L") + 1)) 'Line
      S = VAL(Mid(Temp, INSTR(Temp, "S") + 1)) 'Subroutine
      I = VAL(Mid(Temp, INSTR(Temp, "I") + 1)) 'Subroutine line

      'If F and L = 0, location isn't known
      If F = 0 And L = 0 Then
        OutMessage(PD) = MessageType + OutMessage(PD)

      Else
        'Trim filename of path
        Fi = SourceFile(F).FileName
        FOR T = LEN(Fi) to 1 step -1
          IF Mid(Fi, T, 1) = "\" THEN Fi = Mid(Fi, T + 1): Exit For
        NEXT
        'Prepare message
        If GCGB = 0 Then
          'New handler for SynWrite and GC Code IDEs.
	      ' <file> ** ( ... a system include file (within lowlevel.dat)
	      ' <file> * ( ... an system include  file, like #include <>
	      ' <file> ** ( ... other files.
          Select Case SourceFile(F).SystemInclude
              Case -1
                OutMessage(PD) = Fi + " ** (" + Str(L) + "): " + MessageType + Me
                OutMessage(PD) = OutMessage(PD) +message("SystemIncludeMessage")
              Case 1
                OutMessage(PD) = Fi + " * (" + Str(L) + "): " + MessageType + Me
                OutMessage(PD) = OutMessage(PD) +message("LibraryIncludeMessage")
              Case Else
                'Standard message
                OutMessage(PD) = Fi + " (" + Str(L) + "): " + MessageType + Me
          End Select 
        Else
          'Use special error format for GCGB (or other IDE that breaks program into subs)
          OutMessage(PD) = Left(MessageType, 1) + "|" + Fi + "|" + Str(S) + "|" + Str(I) + "|" + Me
        End If
      End If
    Else
      OutMessage(PD) = MessageType + OutMessage(PD)
    END IF

  Next

  If ERC = 0 And WRC > 0 And WarningsAsErrors <> 0 Then
    'No errors, only warnings, but treating warnings as errors
    LogOutputMessage(Message("TypeError") + ": " + Message("WarningsAsErrors"))
    ERC += 1
  End If

  'Display message
  IF ERC = 1 THEN Temp = Message("Error")
  IF ERC > 1 THEN Temp = Message("Errors")
  IF ERC = 0 And WRC = 1 Then Temp = Message("Warning")
  IF ERC = 0 And WRC > 1 Then Temp = Message("Warnings")

  'If not being called by GCGB/other IDE, display errors
  IF GCGB = 0 THEN
    PRINT
    PRINT Temp
    PRINT
    FOR PD = 1 TO OutMessages
      Temp = OutMessage(PD)
      'Display errors in 77 columns neatly
      '77 chosen because it allows for the whole thing to fit in a Windows command
      'prompt, the screen is normally 80 columns but the scrollbar takes away 3.
  ShowError:
      IF LEN(Temp) <= Columnwidth THEN PRINT SPC(1); Temp
      IF LEN(Temp) > Columnwidth THEN
        FOR T = Columnwidth TO 1 STEP -1
          IF Mid(Temp, T, 1) = " " THEN
            PRINT SPC(1); Left(Temp, T)
            Temp = Mid(Temp, T + 1)
            GOTO ShowError
          END IF
        NEXT
      END IF
    NEXT
  END If

  'Set return code
  If ERC > 0 And ExitValue = 0 Then
    ExitValue = &Hdeadbeef
  End If


  'Write error log
  If ERC > 0 Or( WarningsAsErrors = -1 and WRC > 0) Then
    PRINT
    PRINT Message("ErrorLogged") + ErrorFileName + "."
  End IF
  OPEN ErrorFileName FOR OUTPUT AS #1
  FOR PD = 1 TO OutMessages
    PRINT #1, OutMessage(PD)
  NEXT
  CLOSE
  PRINT

  'Pause if errors found and pause enabled
  IF PauseOnErr = 1 And ErrorsFound Then
    WaitForKeyOrTimeout
  END IF
  
  End Sub

Sub MergeSubroutines

  'Combine subroutines into single program
  Dim As Integer CurrSub, TotalProgSize, FitAttempts, SubsAdded, SubQueueCount, Temp
  Dim As Integer SortPos, MovePos, ThisSub, RequiredSubs, CurrPage, OldTotalProgSize
  Dim As Integer SubsFit, FirstPageFull, ForceFit, CurrPagePos, SortMore, CurrListPage
  Dim As AllocationOrderType AllocationOrder(MAX_PROG_PAGES)
  Dim As SubType Pointer CurrSubPtr
  Dim As LinkedListElement Pointer CurrLine
  Dim As String SubNameOut, ErrTemp, SubLoc

  Dim As SubType Pointer IntSub
  Dim As Integer IntSubLoc

  Dim MaxDestPage as Integer = 0  
  Dim CurrEPTable as Integer
  Dim OrgPosOffset as Integer
  Dim NonChipFamily16DataBlocksNotAdded As Integer = -1

  'Set ForceFit if on AVR or 18F
  'OptimiseCalls and OptimiseIf may reduce the code enough to fit, let the assembler worry if it doesn't
  ForceFit = 0
  If ModeAVR Or ChipFamily = 16 Then
    ForceFit = -1
  End If

  Dim As Integer SubQueue(SBC + 1)
  SubQueueCount = 0

  'Generate call tree of program
  CreateCallTree

  'Get location of interrupt Sub (if it exists)
  IntSub = 0
  IntSubLoc = LocationOfSub("INTERRUPT", "")   'was capitalised
  If IntSubLoc <> 0 Then
    IntSub = Subroutine(IntSubLoc)
  End If

  If ForceFit Then
    'Force subs to fit
    'Put them all on page 1, assembler can deal with the mess
    For Temp = 0 To SBC
      With *Subroutine(Temp)
        If .Required Then
          .LocationSet = -1
          .DestPage = 1

          'Sub size is used for information only on 18F/AVR
          CalcSubSize(Subroutine(Temp))
        End If
      End With
    Next

  Else
    'Don't force subs to fit
    'Try to put them all in place, if they don't fit show an error
    'This should be used on 10F/12F/16F PIC, not on AVR or 18F

    'Measure size of subs
    TotalProgSize = 0
    For CurrSub = 0 To SBC
      If Subroutine(CurrSub)->Required Then
        CalcSubSize(Subroutine(CurrSub))
        TotalProgSize += Subroutine(CurrSub)->HexSize
        'Is subroutine too big to fit anywhere?
        If Subroutine(CurrSub)->HexSize > 2047 Then
          ErrTemp = Message("SubToBigForPage")
          Replace ErrTemp, "%sub%", Subroutine(CurrSub)->Name
          Replace ErrTemp, "%size%", Str(Subroutine(CurrSub)->HexSize)
          LogError ErrTemp, ""
        End If
        'Clear location set flag as well
        Subroutine(CurrSub)->LocationSet = 0
      End If
    Next

    'Get list of subs sorted by size, excluding those forced to page 0
    'Copy list of locations
    RequiredSubs = 0
    For Temp = 0 To SBC
      If Subroutine(Temp)->Required Then
        If Not Subroutine(Temp)->FirstPage Then
          SubQueueCount += 1
          SubQueue(SubQueueCount) = Temp
        End If
        RequiredSubs += 1
      End If
    Next
    'Sort by size
    '(Use insertion sort)
    For SortPos = 1 To SubQueueCount
      ThisSub = SubQueue(SortPos)
      MovePos = SortPos - 1
      'Do While MovePos > 0 And Subroutine(ThisSub)->HexSize > Subroutine(SubQueue(MovePos))->HexSize
      Do While MovePos > 0 And Subroutine(ThisSub)->MaxHexSize > Subroutine(SubQueue(MovePos))->MaxHexSize
        SubQueue(MovePos + 1) = SubQueue(MovePos)
        MovePos -= 1
      Loop
      SubQueue(MovePos + 1) = ThisSub
    Next

    'Clear all pages
    For CurrPage = 1 To ProgMemPages
      ProgMemPage(CurrPage).CodeSize = 0
    Next

    'Try moving subs and counting sizes until everything stabilises
    FitAttempts = 0
    Do
      FitAttempts += 1
      SubsAdded = 0

      'Prepare subroutines for allocation
      For CurrSub = 0 To SBC
        'Clear location set flags on subs
        Subroutine(CurrSub)->LocationSet = 0
        'Clear call counts
        For CurrPage = 0 To ProgMemPages
          Subroutine(CurrSub)->CallsFromPage(CurrPage) = 0
        Next
      Next

      'Clear size counters on pages
      For CurrPage = 1 To ProgMemPages
        ProgMemPage(CurrPage).CodeSize = 0
      Next

      'Position subroutines
      'Page 0 subs
      FirstPageFull = 0
      For CurrSub = 0 To SBC
        If Subroutine(CurrSub)->Required And Subroutine(CurrSub)->FirstPage Then
          Subroutine(CurrSub)->DestPage = 0
          'Print "Placing "; Subroutine(CurrSub)->Name; " on first page"
          ProgMemPage(1).CodeSize += Subroutine(CurrSub)->MaxHexSize
          If ProgMemPage(1).CodeSize < ProgMemPage(1).MaxSize Then
            SubsAdded += 1
            Subroutine(CurrSub)->LocationSet = -1
            Subroutine(CurrSub)->DestPage = 1
            'Record calls to other subroutines
            UpdateOutgoingCalls Subroutine(CurrSub)
          Else
            'Once the first page is full, no point trying to locate anything else
            FirstPageFull = -1
            GoTo StopTryingToFit
          End If
        End If
      Next

      'Other subs
      For CurrSub = 1 To SubQueueCount
        CurrSubPtr = Subroutine(SubQueue(CurrSub))
        'Print "Placing "; CurrSubPtr->Name; " anywhere"
        'Decide which pages are better for this subroutine
        For CurrPage = 1 To ProgMemPages
          AllocationOrder(CurrPage).Page = CurrPage
          AllocationOrder(CurrPage).Calls = CurrSubPtr->CallsFromPage(CurrPage)
        Next
        SortMore = -1
        Do While SortMore
          SortMore = 0
          For CurrPage = 1 To ProgMemPages - 1
            If AllocationOrder(CurrPage).Calls < AllocationOrder(CurrPage + 1).Calls Then
              Swap AllocationOrder(CurrPage), AllocationOrder(CurrPage + 1)
              SortMore = -1
            End If
          Next
        Loop

        'Where will this Sub fit?
        'Note 2/6/2013: Implemented allocation order, and a program went from 7748 to 7761 words
        'Disabled again, more testing needed to see if it produces an overall benefit
        For CurrPage = 1 To ProgMemPages
        'For CurrListPage = 1 To ProgMemPages
        ' CurrPage = AllocationOrder(CurrListPage).Page

          With ProgMemPage(CurrPage)
            If .MaxSize >= .CodeSize + CurrSubPtr->MaxHexSize Then
              'Print , "page "; CurrPage; ", size "; CurrSubPtr->HexSize; ", curr filled "; .CodeSize; "/"; .MaxSize
              'Found a location
              CurrSubPtr->LocationSet = -1
              CurrSubPtr->DestPage = CurrPage
              .CodeSize += CurrSubPtr->MaxHexSize
              SubsAdded += 1
              'Record calls to other subroutines
              UpdateOutgoingCalls Subroutine(CurrSub)
              GoTo LocateNextSub
            End If
          End With
        Next

        LocateNextSub:
      Next

      'Calc new sizes
      OldTotalProgSize = TotalProgSize
      TotalProgSize = 0
      For CurrSub = 0 To SBC
        If Subroutine(CurrSub)->Required Then
          CalcSubSize(Subroutine(CurrSub))
          TotalProgSize += Subroutine(CurrSub)->HexSize
        End If
      Next

      'If everything still fits, can exit
      SubsFit = -1
      For CurrPage = 0 To ProgMemPages
        ProgMemPage(CurrPage).CodeSize = 0
        For CurrSub = 0 To SBC
          With *Subroutine(CurrSub)
            If .Required And .DestPage = CurrPage Then
              If .LocationSet Then
                ProgMemPage(CurrPage).CodeSize += .HexSize
                If ProgMemPage(CurrPage).CodeSize > ProgMemPage(CurrPage).MaxSize Then
                  SubsFit = 0
                  GoTo UnableToFit
                End If
              Else
                SubsFit = 0
                GoTo UnableToFit
              End If
            End If
          End With
        Next
      Next
      UnableToFit:
      If SubsFit And SubsAdded = RequiredSubs Then Exit Do

      'Print "Fit attempt"; FitAttempts; ", page sizes ";
      'For CurrPage = 1 To ProgMemPages
      ' Print Str(CurrPage); ":"; Str(ProgMemPage(CurrPage).CodeSize); " ";
      'Next
      'Print

    Loop While FitAttempts < 20
    StopTryingToFit:

    'If not all subs have been added, show an error
    If Not SubsFit Then
      If FirstPageFull Then
        LogError Message("FirstPageFull")
      Else
        LogError Message("OutOfProgMem")
      End If
    End If
  End If

  'Get a list of subs sorted into alphabetical order
  'Copy list of locations
  SubQueueCount = 0
  For Temp = 0 To SBC
    If Subroutine(Temp)->Required Then
      SubQueueCount += 1
      SubQueue(SubQueueCount) = Temp
    End If
  Next
  'Sort by name
  '(Use insertion sort)
  For SortPos = 2 To SubQueueCount
    ThisSub = SubQueue(SortPos)
    MovePos = SortPos - 1
    Do While MovePos > 1 And Subroutine(ThisSub)->Name < Subroutine(SubQueue(MovePos))->Name
      SubQueue(MovePos + 1) = SubQueue(MovePos)
      MovePos -= 1
    Loop
    SubQueue(MovePos + 1) = ThisSub
  Next

  'Add jumps in required vector locations
  CurrLine = CompilerOutput->CodeEnd
  CurrLine = LinkedListInsertList(CurrLine, GenerateVectorCode)

  'If on page 0 on 10/12/16, and interrupt exists, add it
  If ModePIC Then
    If IntSub <> 0 And ChipFamily <> 16 Then

      'Alter calls in Sub (Use long/short calls as needed)
      AddPageCommands(IntSub)
      'Add Sub code
      CurrLine = LinkedListInsertList(CurrLine, IntSub->CodeStart)
      'Blank, stars, blank at end
      CurrLine = LinkedListInsert(CurrLine, "")
      CurrLine = LinkedListInsert(CurrLine, Star80)
      CurrLine = LinkedListInsert(CurrLine, "")

      'Mark as no longer required so it isn't added again
      IntSub->Required = 0
    End If
  End If

  ' Calculate max page used and how many DATA blocks 
  For CurrSub = 1 To SubQueueCount
    If CurrSubPtr <> 0 Then
      If CurrSubPtr->DestPage > MaxDestPage then 
        MaxDestPage = CurrSubPtr->DestPage
      End If
    End If
  Next
  ' Print "Max Page Used " + Str(MaxDestPage)

  For CurrEPTable = 1 TO DataTables
    With DataTable(CurrEPTable)
    
      If .IsData = -1 Then
        DataBlockCount = DataBlockCount +1
      End If       
    End With
  Next
  'Add all subs to output code
  'Add subs that could be located
 
  For CurrPage = 1 To ProgMemPages
    
    If ( ChipFamily <> 16 And MaxDestPage = CurrPage - 1 and CurrPagePos <> 0 )   Then
          ' Handle the DATABLOCKs here for non 18F or AVR
          AddDataBlocks ( CurrLine, CurrPage, CurrPagePos, DataBlockCount, NonChipFamily16DataBlocksNotAdded  )
          NonChipFamily16DataBlocksNotAdded = 0
    End If

    If UserCodeOnlyEnabled = 0  Then
      If CurrPage > 0 and CurrPage <= ProgMemPages Then
        'Only output this line when there this is normal GCB compilation and there is no data blocks being processsed.
        CurrLine = LinkedListInsert(CurrLine, ";Program_memory_page: " + Str(CurrPage - 1) )
      End If
    End If
    
    If ModePIC Then
      If CurrPage <= ProgMemPages Then 
        If ChipFamily = 16 Or IntSub = 0 Or CurrPage > 1 Then
            If UserCodeOnlyEnabled = 0  Then

              If CurrPage > 1 then
                  CurrLine = LinkedListInsert(CurrLine, " PSECT Progmem" + Str(CurrPage - 1) + ",class=CODE,space=SPACE_CODE,delta=2, abs, ovrld " )
                  GetMetaData(CurrLine)->IsPICAS = -1
              End if

              CurrLine = LinkedListInsert(CurrLine, " ORG " + Str(ProgMemPage(CurrPage).StartLoc))
              CurrPagePos = ProgMemPage(CurrPage).StartLoc

            Else
                'Output as comment
                CurrLine = LinkedListInsert(CurrLine, ";ORG " + Str(ProgMemPage(CurrPage).StartLoc))
            End If
        Else
          'On 10/12/16, Interrupt must go at very start of program
          'Allow it space in page 0.
          If UserCodeOnlyEnabled = 0  Then
              CurrLine = LinkedListInsert(CurrLine, " ORG " + Str(ProgMemPage(CurrPage).StartLoc + IntSub->HexSize))
              CurrPagePos = ProgMemPage(CurrPage).StartLoc + IntSub->HexSize
          Else
            'only output once
            If CurrPage = 1 then
                CurrLine = LinkedListInsert(CurrLine, " ORG " + Str(ProgMemPage(CurrPage).StartLoc + IntSub->HexSize))
                CurrPagePos = ProgMemPage(CurrPage).StartLoc + IntSub->HexSize
            Else
                CurrLine = LinkedListInsert(CurrLine, ";ORG " + Str(ProgMemPage(CurrPage).StartLoc + IntSub->HexSize))
            End  If
          End If
        End If
      End If

      'On 12 bit, insert list of GOTOs to allow calls to second half of pages
      If ChipFamily = 12 Then
        Dim GotoMainInserted As Integer = 0
        CurrLine = LinkedListInsert(CurrLine, ";Indirect jumps to allow calls to second half of page")
        For CurrSub = 1 To SubQueueCount
          CurrSubPtr = Subroutine(SubQueue(CurrSub))
          If CurrSubPtr->Required And CurrSubPtr->LocationSet And CurrSubPtr->DestPage = CurrPage Then
            'Need to put the Sub here
            'Name of sub
            If SubQueue(CurrSub) <> 0 Then
              SubNameOut = GetSubFullName(SubQueue(CurrSub))
              CurrPagePos += 1
              StatsUsedProgram += 1

              If Not GotoMainInserted Then
                CurrLine = LinkedListInsert(CurrLine, " goto BASPROGRAMSTART")
                GotoMainInserted = -1
              End If

              CurrLine = LinkedListInsert(CurrLine, SubNameOut)
              GetMetaData(CurrLine)->IsLabel = -1
              CurrLine = LinkedListInsert(CurrLine, " goto SysInd_" + SubNameOut)
            End If

          End If
        Next
        CurrLine = LinkedListInsert(CurrLine, "")
      End If

    ElseIf ModeAVR Then
      CurrLine = LinkedListInsert(CurrLine, ".ORG " + Str(ProgMemPage(CurrPage).StartLoc))
      CurrPagePos = ProgMemPage(CurrPage).StartLoc
    ElseIf ModeZ8 Then
      CurrLine = LinkedListInsert(CurrLine, " ORG " + Str(ProgMemPage(CurrPage).StartLoc))
      CurrPagePos = ProgMemPage(CurrPage).StartLoc
    End If

    'check for #REQUIRED PARAMETERS
    For CurrSub = 1 To SubQueueCount
      CurrSubPtr = Subroutine(SubQueue(CurrSub))
      If CurrSubPtr->Required And CurrSubPtr->LocationSet And CurrSubPtr->DestPage = CurrPage Then

        'Check for required parameters
        Dim RequiredString as String
        Dim MandatedStringMessage as String
        Dim OutMessage as String
        Dim RequiredCommand as String
        Dim As String ReqChip, ReqConst

        Dim As LinkedListElement Pointer RequiredListPos, ItemList, ItemListPos
        Dim ReqListItem As String

        'Get array of REQUIRED pieces from current file
        Dim SourceFileNo As Integer = CurrSubPtr->SourceFile
        RequiredListPos = 0
        If SourceFile(SourceFileNo).RequiredModules <> 0 Then
          RequiredListPos = SourceFile(SourceFileNo).RequiredModules->Next
        End If
        Do While RequiredListPos <> 0 and RequiredModuleCheck = 1

          RequiredCommand = RequiredListPos->Value
          ItemList = GetElements(RequiredCommand, " ")
          ItemListPos = ItemList->Next
          If LinkedListSize(ItemList) >= 3 Then
            ' Req_Res(0)  will always be REQUIRED
            ItemListPos = ItemListPos->Next
            ' Req_Res(1)  PIC or AVR
            ReqChip = ItemListPos->Value
            ItemListPos = ItemListPos->Next
            ' Req_Res(2)  what we are looking for ... a constant in the DAT file
            ReqConst = ItemListPos->Value
            ItemListPos = ItemListPos->Next

            'Build the string  from element 2 onwards
            RequiredString = ""
            Do While ItemListPos <> 0
              RequiredString = RequiredString + ItemListPos->Value
              ItemListPos = ItemListPos->Next
            Loop

            IF (UCASE(ReqChip) = "PIC" AND ModePIC) or (UCASE(ReqChip) = "AVR" AND ModeAVR) Then
              'Do we have SFR?
              IF Not HasSFR(ReqConst) Then
                'Do we have no constant, or a constant that's zero?
                If Val(HashMapGetStr(Constants, UCase(ReqConst))) = 0 Then

                  If Instr(RequiredString, ";STRING" ) <> 0 Then
                      MandatedStringMessage = GetString(RequiredString)
                      OutMessage = Message("BadMandatedOption")
                      Replace OutMessage, "%string%", MandatedStringMessage
                  End if

                  If Left(RequiredString, 1 ) = "%"  Then
                    MandatedStringMessage = RequiredString
                    Replace MandatedStringMessage , "%", ""
                    Replace MandatedStringMessage , "%", ""
                    OutMessage = Message(MandatedStringMessage)
                  End if
                  ErrorsFound = -1
                  LogOutputMessage mid( SourceFile(SourceFileNo).filename, 1+instrRev(SourceFile(SourceFileNo).filename,"\")  )+" (0): Error: "+OutMessage
                End if
              End If
             end if

          Else
            'Malformed required list

          End If

          RequiredListPos = RequiredListPos->Next
        Loop

        'Need to put the Sub here
        'Name of sub
        If SubQueue(CurrSub) = 0 Then
          If UserCodeOnlyEnabled = 0 Then
              SubNameOut = "BASPROGRAMSTART"
          End if
        Else
          SubNameOut = GetSubFullName(SubQueue(CurrSub))
          If ChipFamily = 12 Then
            SubNameOut = "SysInd_" + SubNameOut
          End If
        End If
        ' Print "Placing " + SubNameOut + " at 0x" + Hex(CurrPagePos) + " (size:" + Str(CurrSubPtr->HexSize) + ")"
        CurrPagePos += CurrSubPtr->HexSize
        StatsUsedProgram += CurrSubPtr->HexSize

        SubLoc = GetFileLine(CurrSubPtr->Origin)
        If PreserveMode = 2 Then
            If CurrSubPtr->Overloaded Then
              CurrLine = LinkedListInsert(CurrLine, ";Overloaded signature: " + GetSubSig(*CurrSubPtr) + ", Source: " + SubLoc)
            Else
              If SubLoc <> "" Then CurrLine = LinkedListInsert(CurrLine, ";Source: " + SubLoc)
            End If
        End If
        'CurrLine = LinkedListInsert(CurrLine, ";Subroutine size:" + Str(CurrSubPtr->HexSize) + " words")
        CurrLine = LinkedListInsert(CurrLine, SubNameOut + LabelEnd)
        GetMetaData(Currline)->IsLabel = -1
        'Alter calls in Sub (Use long/short calls as needed)
        AddPageCommands(CurrSubPtr)
        'Add Sub code
        CurrLine = LinkedListInsertList(CurrLine, CurrSubPtr->CodeStart)
        'Return
        If Not CurrSubPtr->NoReturn Then
          If ModePIC Then
            If ChipFamily = 12 Then
              CurrLine = LinkedListInsert(CurrLine, " retlw 0")
            Else
              CurrLine = LinkedListInsert(CurrLine, " return")
            End If
          ElseIf ModeAVR Then
            CurrLine = LinkedListInsert(CurrLine, " ret")
          ElseIf ModeZ8 Then
            CurrLine = LinkedListInsert(CurrLine, " ret")
          End If
        End If
        'Blank, stars, blank at end
        CurrLine = LinkedListInsert(CurrLine, "")
        CurrLine = LinkedListInsert(CurrLine, Star80)
        CurrLine = LinkedListInsert(CurrLine, "")
      End If
    Next

    'This is needed as in there is NO GCB source.
    If NonChipFamily16DataBlocksNotAdded And MaxDestPage = CurrPage - 1 Then 
      AddDataBlocks ( CurrLine, CurrPage, CurrPagePos, DataBlockCount, NonChipFamily16DataBlocksNotAdded  )
      NonChipFamily16DataBlocksNotAdded = 0
    End If

  Next
  

  'Add any subs that couldn't be located to end of file
  'User may be able to manually locate them
  Dim As Integer FirstUnsetSub
  FirstUnsetSub = -1
  'For CurrSub = 0 To SBC
  For CurrSub = 1 To SubQueueCount
    CurrSubPtr = Subroutine(SubQueue(CurrSub))
    If CurrSubPtr->Required And Not CurrSubPtr->LocationSet Then
      If FirstUnsetSub Then
        CurrLine = LinkedListInsert(CurrLine, ";Subs that could not be automatically placed")
        CurrLine = LinkedListInsert(CurrLine, ";It may be possible to manually locate them, but you should upgrade to a larger chip if possible")
        CurrLine = LinkedListInsert(CurrLine, "")
        FirstUnsetSub = 0
      End If
      'Need to put the Sub here
      'Name of sub
      If SubQueue(CurrSub) = 0 Then
        SubNameOut = "BASPROGRAMSTART"
      Else
        SubNameOut = GetSubFullName(SubQueue(CurrSub))
      End If
      CurrLine = LinkedListInsert(CurrLine, SubNameOut + LabelEnd)
      GetMetaData(Currline)->IsLabel = -1
      CurrLine = LinkedListInsert(CurrLine, ";This sub size:" + Str(CurrSubPtr->HexSize))
      StatsUsedProgram += CurrSubPtr->HexSize
      'Add Sub code
      CurrLine = LinkedListInsertList(CurrLine, CurrSubPtr->CodeStart)
      'Return
      If Not CurrSubPtr->NoReturn Then
        If ModePIC Then
          CurrLine = LinkedListInsert(CurrLine, " return")
        ElseIf ModeAVR Then
          CurrLine = LinkedListInsert(CurrLine, " ret")
        ElseIf ModeZ8 Then
          CurrLine = LinkedListInsert(CurrLine, " ret")
        End If
      End If
      'Blank, stars, blank at end
      CurrLine = LinkedListInsert(CurrLine, "")
      CurrLine = LinkedListInsert(CurrLine, Star80)
      CurrLine = LinkedListInsert(CurrLine, "")
    End If
  Next

  If ChipFamily = 16 or MODEAVR Then 
    AddDataBlocks ( CurrLine, CurrPage, CurrPagePos, DataBlockCount, NonChipFamily16DataBlocksNotAdded )
  End If

  ' *WRITE EEPROM* *WRITE TABLE*
  'Add EEPROM data tables
  Dim As Integer EPDataHeader, EPDataLoc, CurrEPItem, TableAddressState, AVRAddressState
  Dim As String EPTempData
  Dim As Integer EPAddress, DataTableSwapped
  Dim As DataTableType Temp_DataTableType
  Dim As Integer TableUSedCounter 
  EPDataHeader = 0
  TableAddressState = 0
  AVRAddressState = 0
  TableUSedCounter = 0

  IF DataTables > 0 Then

    'Sort table so GCASM handles TABLE and EEPROM Datasets ORG correctly.
    Do
      DataTableSwapped = 0 
      For CurrEPTable = 1 TO DataTables - 1
        If DataTable(CurrEPTable).Used And DataTable(CurrEPTable).StoreLoc = 1 Then
            If DataTable(CurrEPTable).FixedLoc > DataTable(CurrEPTable+1).FixedLoc Then
              Swap   DataTable(CurrEPTable) , DataTable(CurrEPTable+1)
              DataTableSwapped = -1
            End If
        End IF
      Next
    Loop While DataTableSwapped = -1


    For CurrEPTable = 1 TO DataTables
      If DataTable(CurrEPTable).Used And DataTable(CurrEPTable).StoreLoc = 1 Then

        'Count used tables
        TableUSedCounter = TableUSedCounter + 1

        'Add header (if not added)
        If Not EPDataHeader Then
          CurrLine = LinkedListInsert(CurrLine, Star80)
          CurrLine = LinkedListInsert(CurrLine, "")

          If ChipFamily = 12 Or ChipFamily = 14 Then
            CurrLine = LinkedListInsert(CurrLine, "; Data Tables (data memory)")
            'CurrLine = LinkedListInsert(CurrLine, " ORG 0x2100")
            EPAddress = &h2100

          ElseIf ChipFamily = 15 Then
            CurrLine = LinkedListInsert(CurrLine, "; Data Tables (data memory)")
            'CurrLine = LinkedListInsert(CurrLine, " ORG 0xF000")
            EPAddress = &hF000
          ElseIf ChipFamily = 16 Then
            If ( ChipSubFamily = ChipFamily18FxxQ43  )  then
              CurrLine = LinkedListInsert(CurrLine, "; Data Tables (ChipFamily18FxxQ43 EEPROM Address 0x380000)")
              'CurrLine = LinkedListInsert(CurrLine, " ORG 0x380000")
              EPAddress = &h380000
            ElseIf ( ChipSubFamily = ChipFamily18FxxQ41 )  then
              CurrLine = LinkedListInsert(CurrLine, "; Data Tables (ChipFamily18FxxQ41 EEPROM Address 0x380000)")
              'CurrLine = LinkedListInsert(CurrLine, " ORG 0x380000")
              EPAddress = &h380000
            ElseIf ( ChipSubFamily = ChipFamily18FxxK40  )  then
              CurrLine = LinkedListInsert(CurrLine, "; Data Tables (ChipFamily18FxxK40 EEPROM Address 0x310000)")
              'CurrLine = LinkedListInsert(CurrLine, " ORG 0x310000")
              EPAddress = &h310000
            ElseIf ( ChipSubFamily = ChipFamily18FxxQ10  )  then
              CurrLine = LinkedListInsert(CurrLine, "; Data Tables (ChipFamily18FxxQ10 EEPROM Address 0x310000)")
              'CurrLine = LinkedListInsert(CurrLine, " ORG 0x310000")
              EPAddress = &h310000
            ElseIf ( ChipSubFamily = ChipFamily18FxxQ40  )  then
              CurrLine = LinkedListInsert(CurrLine, "; Data Tables (ChipFamily18FxxQ40 EEPROM Address 0x380000)")
              'CurrLine = LinkedListInsert(CurrLine, " ORG 0x380000")
              EPAddress = &h380000
            ElseIf ( ChipSubFamily = ChipFamily18FxxQ71 )  then
              CurrLine = LinkedListInsert(CurrLine, "; Data Tables (ChipFamily18FxxQ71 EEPROM Address 0x380000)")
              'CurrLine = LinkedListInsert(CurrLine, " ORG 0x380000")
              EPAddress = &h380000
            ElseIf ( ChipSubFamily = ChipFamily18FxxQ83 )  then
              CurrLine = LinkedListInsert(CurrLine, "; Data Tables (ChipFamily18FxxQ83 EEPROM Address 0x380000)")
              'CurrLine = LinkedListInsert(CurrLine, " ORG 0x380000")
              EPAddress = &h380000    
            ElseIf ( ChipSubFamily = ChipFamily18FxxQ20 )  then
              CurrLine = LinkedListInsert(CurrLine, "; Data Tables (ChipFamily18FxxQ20 EEPROM Address 0x380000)")
              'CurrLine = LinkedListInsert(CurrLine, " ORG 0x380000")
              EPAddress = &h380000                     
            ElseIf ( ChipSubFamily = ChipFamily18FxxQ24 )  then
              CurrLine = LinkedListInsert(CurrLine, "; Data Tables (ChipFamily18FxxQ24 EEPROM Address 0x380000)")
              'CurrLine = LinkedListInsert(CurrLine, " ORG 0x380000")
              EPAddress = &h380000                                         
            ElseIf ChipFamilyVariant = 1 then
              CurrLine = LinkedListInsert(CurrLine, "; Data Tables (ChipFamilyVariant EEPROM Address 0x310000)")
              'CurrLine = LinkedListInsert(CurrLine, " ORG 0x310000")
              EPAddress = &h380000
            Else
              ' this does not support AVR, see https://microchip.my.site.com/s/article/EEPROM-Segment-Preloading-in-Assembly-code for correct format
              CurrLine = LinkedListInsert(CurrLine, "; Data Tables (Default EEPROM address)")
              'CurrLine = LinkedListInsert(CurrLine, " ORG 0xF00000")
              EPAddress = &hF00000
            End if
            
          End If
          EPDataHeader = -1
          EPDataLoc = 0
        End If
        
        With DataTable(CurrEPTable)

          If .Items > 0 OR .IsEEPromData = 0 Then

          'Construct string using a variable
          Dim EETempVal as integer
          EETempVal = EPAddress+.FixedLoc
          If ModePIC Then
            If TableAddressState = 0 or .IsEEPromData Then
              'only show the ORG for table address once.
              TableAddressState = - 1
              CurrLine = LinkedListInsert(CurrLine, "ORG 0x"+HEX(EETempVal))
            End If
          Else
            If AVRAddressState = 0 or .IsEEPromData Then
              'only show the ORG for table address once.
              AVRAddressState = - 1
              If FirstESEG = 0 Then 
                CurrLine = LinkedListInsert(CurrLine, ".ESEG")
                FirstESEG = 1
              End If
              CurrLine = LinkedListInsert(CurrLine, ".ORG 0x"+Right("0"+HEX(EETempVal),2))              
            End If
          End If


            'Get data
            
            If .IsEEPromData = 0 Then 
              'Output the size of the table, not, required for EEData
              EPTempData = "0x"+Right("0"+Hex(.Items),2)
            Else
              EPTempData = ""
            End If
            
            For CurrEPItem = 1 To .Items
              EPTempData = EPTempData + ", 0x" + Right("0"+HEX(.Item(CurrEPItem)),2)
            Next
            If Left(EPTempData, 2 ) = ", " Then EPTempData = Mid(EPTempData,3, Len( EPTempData ) ) 
            'Add table
            ToAsmSymbols += 1
            If .IsEEPromData = 0 then
              ToAsmSymbol(ToAsmSymbols, 1) = " TABLE" + Trim(.Name)
            Else
              ToAsmSymbol(ToAsmSymbols, 1) = " DATA" + Trim(.Name)
            End If
            'This test ensure the address of the DATA is ALIGNed to 2 for ChipFamily = 16 
            If (  EPDataLoc/2 <> Int(EPDataLoc/2) and ChipFamily = 16 ) Then
                EPDataLoc = EPDataLoc + 1
            End If
            ToAsmSymbol(ToAsmSymbols, 2) = Str(EPDataLoc)

            if instr(UCase(AsmExe),"PIC-AS") = 0 then
              If .IsEEPromData = 0 then
                If MODEPIC Then CurrLine = LinkedListInsert(CurrLine, " TABLE" + Trim(.Name) + " equ " + Str(EPDataLoc))
                If MODEAVR Then CurrLine = LinkedListInsert(CurrLine, " TABLE" + Trim(.Name) + ":")
              Else
                If MODEPIC Then CurrLine = LinkedListInsert(CurrLine, "DATA" + Trim(.Name) + " equ " + Str(EPDataLoc))
                If MODEAVR Then CurrLine = LinkedListInsert(CurrLine, " DATA" + Trim(.Name) + ":")
              End If
              GetMetaData(Currline)->IsLabel = -1
              If trim(EPTempData) <> "" Then 
                'Do not push out empty structure, as this will cause an error in MPASM etc
                If Instr(UCase(AsmExe), "MPASM") > 0 Then

                  If ModeAVR Then
                    LogWarning "Using MPASM with AVR - GCBASIC will produce incorrect ASM"
                  End If

                  If Len(EPTempData) < 129 Then
                    CurrLine = LinkedListInsert(CurrLine, "  de " + EPTempData)
                  Else
                    ' Only output width that MPASM can cope with
                    EPTempData = EPTempData +","  'add additional delimter to make this routine work

                    Dim as Integer EEStringCut, EELastDelimiter 
                    For EEStringCut = 1 to Len(EPTempData) step 86
                      EELastDelimiter = InStrRev( Mid ( EPTempData, EEStringCut, 85 ), "," ) - 1
                      'Found EELastDelimiter so adjust loop
                      if EELastDelimiter <> -1 Then 
                        CurrLine = LinkedListInsert(CurrLine, "  de " + Mid ( EPTempData, EEStringCut, EELastDelimiter ) )
                        EEStringCut =  EEStringCut - ( 86 - EELastDelimiter) + 2
                      End If
                    Next
                  End if
                Else
                  If ModePIC Then CurrLine = LinkedListInsert(CurrLine, "  de " + EPTempData)
                  If ModeAVR Then CurrLine = LinkedListInsert(CurrLine, "  .DB " + EPTempData)
                End If
              End If
            else
              If .IsEEPromData = 0 then
                If MODEPIC Then
                  CurrLine = LinkedListInsert(CurrLine, " TABLE" + Trim(.Name))
                Else
                  CurrLine = LinkedListInsert(CurrLine, " TABLE" + Trim(.Name)+":")
                End if
              Else       
                If MODEPIC Then
                  CurrLine = LinkedListInsert(CurrLine, " DATA" + Trim(.Name))
                Else
                  CurrLine = LinkedListInsert(CurrLine, " DATA" + Trim(.Name)+":")
                End If
              End If
              GetMetaData(Currline)->IsLabel = -1

              If MODEPIC Then
                CurrLine = LinkedListInsert(CurrLine, "  db " + EPTempData)
              Else
                CurrLine = LinkedListInsert(CurrLine, "  .db " + EPTempData)
              End If
            end if

            EPDataLoc += (.Items + 1)
          Else
            'Delete the comment line
            LinkedListDelete(CurrLine)
          End IF
        End With
        'move down as we dont need to align on every table
        IF AFISupport = 1  and ModePIC and ChipFamily = 16 Then
          'There should only one table of this type, and, align may not be needed... but, I do not know if this is the only once....
          CurrLine = LinkedListInsert(CurrLine, " ALIGN 2 ; ASM 2-byte boundary alignment..")
        End if

      End If

    Next

    IF AFISupport = 1  and ModePIC and ChipFamily = 16 Then
      If TableUSedCounter > 0 Then
        'There should only one table of this type, and, align may not be needed... but, I do not know if this is the only once....
        CurrLine = LinkedListInsert(CurrLine, " ALIGN 2; ASM 2-byte boundary alignment...")
      End If
    End if

  End If

  'Check for overlapping tables and datasets
  'Iterate through tables, if EE ( .StoreLoc = 1 ), take the base address add the items if less than the next valid address then all is good, else error message
  Dim as Integer EETempVal, EENextLocation
  For CurrEPTable = 1 TO DataTables - 1
    If DataTable(CurrEPTable).Used And DataTable(CurrEPTable).StoreLoc = 1 Then
      ' Extract next valid table that is EEdata
      EETempVal = CurrEPTable + 1
      EENextLocation = 0
      Do While DataTable(EETempVal).StoreLoc = 0 and EETempVal < DataTables
        EETempVal = EETempVal + 1
      Loop 
      'This is the next EE address
      EENextLocation = DataTable(EETempVal).FixedLoc
        'show the calcs
        ' print DataTable(CurrEPTable).FixedLoc,  DataTable(CurrEPTable).Items, EENextLocation, ( DataTable(CurrEPTable).FixedLoc + DataTable(CurrEPTable).Items )  <= EENextLocation 
      If ( NOT ( DataTable(CurrEPTable).FixedLoc + DataTable(CurrEPTable).Items )  <= EENextLocation ) AND EENextLocation <> 0 Then
          ErrTemp = Message("EEBadORG")
          Replace ErrTemp, "%loc%", "0x"+hex( EPAddress + DataTable(CurrEPTable+1).FixedLoc)
          LogError("GCASM:" + ErrTemp, "")
      End iF
    End If
  Next

  CompilerOutput->CodeEnd = CurrLine
  
  End Sub

Function Message (InData As String) As String

  Dim As String Temp, MsgOut, MsgID
  Dim As Integer DS, T

  'Find message
  MsgID = UCase(InData)
  DS = 0
  FOR T = 1 TO MSGC
    IF Messages(1, T) = MsgID THEN DS = T: EXIT FOR
  NEXT

  'Retrieve message
  IF DS = 0 THEN
    Return "Message " + MsgID + " not defined in messages.dat!"
  Else
    MsgOut = Messages(2, DS)
  End If

  'Replace values
  IF INSTR(UCase(MsgOut), "%SHORTFILENAME%") <> 0 THEN 
    Replace MsgOut, "%SHORTFILENAME%", Mid(FI, InstrRev(FI, "\")+1) 
  End If
  IF INSTR(UCase(MsgOut), "%VERSION%") <> 0 THEN Replace MsgOut, "%VERSION%", Version
  IF INSTR(UCase(MsgOut), "%FILENAME%") <> 0 THEN Replace MsgOut, "%FILENAME%", FI
  IF INSTR(UCase(MsgOut), "%OUTPUTFILE%") <> 0 THEN Replace MsgOut, "%OUTPUTFILE%", OFI
  IF INSTR(UCase(MsgOut), "%CHIPNAME%") <> 0 THEN Replace MsgOut, "%CHIPNAME%", ChipName
  

  Return MsgOut
  END FUNCTION

Function ModeAVR As Integer
  If ChipFamily >= 100 And ChipFamily < 200 Then Return -1
  Return 0
  End Function

Function ModePIC As Integer
  If ChipFamily > 0 And ChipFamily < 100 Then Return -1
  Return 0
  End Function

Function ModeZ8 As Integer
  If ChipFamily > 200 And ChipFamily < 300 Then Return -1
  Return 0
  End Function

Function NewCodeSection As CodeSection Pointer
  Dim As CodeSection Pointer OutSection

  OutSection = Callocate(SizeOf(CodeSection))
  OutSection->CodeList = LinkedListCreate
  OutSection->CodeEnd = OutSection->CodeList

  Return OutSection
  End Function

Function NewProgLineMeta As ProgLineMeta Pointer
  Dim As ProgLineMeta Pointer OutMeta

  OutMeta = Callocate(SizeOf(ProgLineMeta))

  With (*OutMeta)
    .RequiredBank = -1

    .IsAutoPageSel = 0

    .PrevCommands = LinkedListCreate
    .NextCommands = LinkedListCreate
  End With

  Return OutMeta
  End Function

Function NewSubroutine(SubName As String) As SubType Pointer
  Dim As SubType Pointer OutSub

  OutSub = Callocate(SizeOf(SubType))
  With (*OutSub)
    .Name = SubName
    .CodeStart = LinkedListCreate
    .CallList = LinkedListCreate
    .CallerList = LinkedListCreate
    .TemporaryVars = LinkedListCreate

    .IntStateSaveVar = ""
    .ReturnAlias = ""
  End With

  Return OutSub
  End Function

Function TypeOfVar (VarName As String, CurrSub As SubType Pointer) As String

  Dim As String Temp, TempType, Source
  Dim As String GlobalType, LocalType
  Dim As Integer SubLoc
  Dim As VariableType Pointer FoundVar
  Dim As SubType Pointer MainSub
  Dim As Single TempValue

  'Get pointer to main sub
  MainSub = Subroutine(0)
  'Do this to prevent null pointer access
  If CurrSub = 0 Then
    LogError( "'"+Varname+"'", Message("InternalError") )

    CurrSub = MainSub
  End If

  'Bit variables with . override all
  If INSTR(VarName, ".") <> 0 Then TypeOfVar = "BIT": Exit Function

  'Type overrides
  If INSTR(VarName, "[") <> 0 Then
    If INSTR(LCase(VarName), "[double]") <> 0 Then Return "DOUBLE"
    If INSTR(LCase(VarName), "[single]") <> 0 Then Return "SINGLE"
    If INSTR(LCase(VarName), "[long]") <> 0 Then Return "LONG"
    If INSTR(LCase(VarName), "[integer]") <> 0 Then Return "INTEGER"
    If INSTR(LCase(VarName), "[word]") <> 0 Then Return "WORD"
    If INSTR(LCase(VarName), "[byte]") <> 0 Then Return "BYTE"
  End If

  'Handle constants
  If IsConst(VarName) Then
    If InStr(VarName, "@") <> 0 Then
      Return "WORD"
    Else
      TempValue = MakeDec(VarName)
      If TempValue = 0 Or TempValue = 1 Then Return "BIT"
      If TempValue >= 2 And TempValue <= 255 Then Return "BYTE"
      If TempValue >= 256 And TempValue <= 65535 Then Return "WORD"
      If TempValue >= -32768 And TempValue <= -1 Then Return "INTEGER"
      IF TempValue <> INT(TempValue) Then Return "SINGLE"
    End If
  End If

  'Temporary Calculation vars
  IF Left(UCase(VarName), 7) = "SYSTEMP" Then
    Temp = CalcVars(Val(Mid(VarName, 8))).CurrentType
    If Temp = "" Then Temp = "BYTE"
    Return Temp
  End If

  'Element is a function
  If UCase(VarName) = UCase(CurrSub->Name) And CurrSub->IsFunction Then
    Return CurrSub->ReturnType

  Else
    SubLoc = LocationOfSub(VarName, "", , -1)
    'Don't use overloaded type
    If SubLoc > 0 Then
      If Subroutine(SubLoc)->IsFunction Then
        With *(Subroutine(SubLoc))
          If .ReturnType <> "" Then
            Return .ReturnType
          End If
        End With
      End If
    End If
  End If

  'Get local type
  LocalType = "BYTE"
  GlobalType = "BYTE"
  If CurrSub <> MainSub Then

    'To proceed any further, Sub needs to have dim commands compiled
    If Not CurrSub->VarsRead Then CompileDim(CurrSub)

    'Search var list
    FoundVar = HashMapGet(@(CurrSub->Variables), UCase(VarName))
    If FoundVar <> 0 Then
      LocalType = Trim(UCase(FoundVar->Type))
    End If
  End If

  'Permanent calculation vars
  If UCase(Left(VarName, 11)) = "SYSCALCTEMP" And LEN(VarName) = 12 And LocalType = "BYTE" Then
    Temp = UCase(Mid(VarName, 12, 1))
    TempType = ""
    IF Temp = "A" Then TempType = CalcTempType(1)
    IF Temp = "B" Then TempType = CalcTempType(2)
    IF Temp = "X" Then TempType = CalcTempType(3)
    IF TempType <> "" THEN Return TempType
  End If

  'Get global type
  FoundVar = HashMapGet(@(MainSub->Variables), UCase(VarName))
  If FoundVar <> 0 Then
    GlobalType = Trim(UCase(FoundVar->Type))
  End If

  'Get highest of local and global type
  '(Unless type in main Sub is BIT, then it is a bit)
  If CastOrder(LocalType) > CastOrder(GlobalType) And GlobalType <> "BIT" Then
    Return LocalType
  Else
    Return GlobalType
  EndIf
  
  End Function

Function TypeOfValue (ValueNameIn As String, CurrentSub As SubType Pointer, SingCharString As Integer = 0) As String
  'SingCharString: 0 = treat single char as byte, -1 = treat single char as string

  Dim As String ThisType, FinalType, Temp, ValueTemp, CurrentItem, CurrChar
  Dim As String ValueName
  Dim As Integer TCC, SS, CD, FindSub, SubLoc
  Dim TempValue As Double
  Dim TypeCheck( TYPECHECKSIZE ) As String
  TCC = 0
  FinalType = ""

  'If no value, don't return a type
  If ValueNameIn = "" Then Return ""
  ValueName = ValueNameIn

  'Type overrides
  If Left(ValueName, 1) = "[" And InStr(ValueName, "]") <> 0 Then
    ThisType = Mid(ValueName, 2)
    ThisType = Left(ThisType, InStr(ThisType, "]") - 1)
    Return UCase(Trim(ThisType))
  End If

  'Is whole thing a constant?
  If IsConst(ValueName) Then
    If InStr(ValueName, "@") <> 0 Then
      ThisType = "WORD"
    Else
      TempValue = MakeDec(ValueName)
      If TempValue = 0 Or TempValue = 1 Then
        ThisType = "BIT"
      ElseIf TempValue >= 2 And TempValue <= 255 Then
         ThisType = "BYTE"
      ElseIf TempValue >= 256 And TempValue <= 65535 Then
        ThisType = "WORD"
      ElseIf TempValue >= -32768 And TempValue <= -1 Then
        ThisType = "INTEGER"
      ElseIf TempValue >= 0 And TempValue <= (2 ^ 32 - 1) Then
        ThisType = "LONG"
      End If

      IF TempValue <> INT(TempValue) Then ThisType = "SINGLE"
      'If InStr(UCase(ValueName), ";STRING") <> 0 Then ThisType = "STRING"
      If InStr(UCase(ValueName), ";STRING") <> 0 Then
        If Len(GetString(ValueName)) = 1 And Not SingCharString Then
          ThisType = "BYTE"
        Else
          ThisType = "STRING"
      End If
      End If
    End If
    Return ThisType
  End If

  'Remove Function parameters and array indexes
  'Find (
  'Then check for an operator or start of string before (
  'If no operator, remove from ( to matching ) inclusive
  If InStr(ValueName, "(") Then
    Do While InStr(ValueName, " ") <> 0: Replace ValueName, " ", "": Loop
    Dim As Integer DelStart, DelEnd, DelLevel, DelPos
    DelPos = 1
    'Find (
    Do While DelPos < Len(ValueName)
      CurrChar = Mid(ValueName, DelPos, 1)
      If CurrChar = "(" Then
        'Is there a character before ( ?
        If DelPos <> 1 Then
          'Is there something other than a symbol before (
          If Not IsDivider(Mid(ValueName, DelPos - 1, 1)) Then
            'There is a something other than an operator before (
            'So we have found an array index or Function param
            'Find end of index/params
            DelStart = DelPos
            DelLevel = 0
            DelEnd = DelStart
            Do While DelEnd <= Len(ValueName)
              CurrChar = Mid(ValueName, DelEnd, 1)
              If CurrChar = "(" Then
                DelLevel += 1
              ElseIf CurrChar = ")" Then
                DelLevel -= 1
                If DelLevel = 0 Then
                  Exit Do
                End If
              End If
              DelEnd += 1
            Loop
            'Remove everything from start to end
            ValueName = Left(ValueName, DelStart - 1) + Chr(31) + Mid(ValueName, DelEnd + 1)
          End If
        End If
      End If

      DelPos += 1
    Loop
  End If

  'Split into individual elements
  CurrentItem = ""
  For SS = 1 To LEN(ValueName)
    Temp = Mid(ValueName, SS, 1)
    If IsDivider(Temp) AND Temp <> "." AND Temp <> ";" AND Temp <> "$" And Temp <> "[" And Temp <> "]" Then
      IF Trim(CurrentItem) <> "" Then
        TCC += 1
        If TCC > TYPECHECKSIZE Then
            'fatal error
            LogError "TYPECHECKSIZE exceed - reduce complexity: "+ValueNameIn
            WriteErrorLog
            ErrorsFound = - 1
            end
        End if
        TypeCheck(TCC) = Trim(CurrentItem)
        CurrentItem = ""
      End If
    Else
      CurrentItem = CurrentItem + Temp
    End If
  Next
  If Trim(CurrentItem) <> "" Then
    TCC += 1
      If TCC > TYPECHECKSIZE Then
        'fatal error
        LogError "TYPECHECKSIZE exceed - reduce complexity: "+ValueNameIn
        WriteErrorLog
        ErrorsFound = - 1
        end
    End if    
    TypeCheck(TCC) = Trim(CurrentItem)
  End If

  'Check each element
  Dim HasIndex As Integer
  For CD = 1 To TCC
    ValueTemp = TypeCheck(CD)
    HasIndex = 0
    If Right(ValueTemp, 1) = Chr(31) Then
      HasIndex = -1
      ValueTemp = Left(ValueTemp, Len(ValueTemp) - 1)
    End If
    ThisType = ""

    'Element is a string const
    If INSTR(ValueTemp, ";STRING") <> 0 Then
      If Len(GetString(ValueTemp)) = 1 And Not SingCharString Then
        ThisType = "BYTE"
      Else
        ThisType = "STRING"
      End If

    'Element has a cast
    ElseIf InStr(ValueTemp, "[") <> 0 And InStr(ValueTemp, "]") <> 0 Then
      ValueTemp = Mid(ValueTemp, InStr(ValueTemp, "[") + 1)
      ValueTemp = Left(ValueTemp, InStr(ValueTemp, "]") - 1)
      ThisType = UCase(Trim(ValueTemp))

    'Element is a const
    ElseIf IsConst(ValueTemp) Then
      If InStr(ValueTemp, "@") <> 0 Then
        ThisType = "WORD"
      Else
        TempValue = MakeDec(ValueTemp)
        If TempValue = 0 Or TempValue = 1 Then
          ThisType = "BIT"
        ElseIf TempValue >= 2 And TempValue <= 255 Then
           ThisType = "BYTE"
        ElseIf TempValue >= 256 And TempValue <= 65535 Then
          ThisType = "WORD"
        ElseIf TempValue >= -32768 And TempValue <= -1 Then
          ThisType = "INTEGER"
        ElseIf TempValue >= 0 And TempValue <= (2 ^ 32 - 1) Then
          ThisType = "LONG"
        End If

        IF TempValue <> INT(TempValue) Then ThisType = "SINGLE"
      End If

    'Element is a string var, ending with $
    ElseIf Right(ValueTemp, 1) = "$" Then
      ThisType = "STRING"
      If HasIndex Then ThisType = "BYTE"

    'Element is a bit var
    ElseIf InStr(ValueTemp, ".") <> 0 And Not IsConst(Left(ValueTemp, InStr(ValueTemp, ".") - 1)) Then
      ThisType = "BIT"

    'Element is a var (or Function result)
    Else
      ThisType = TypeOfVar(ValueTemp, CurrentSub)
      'May be accessing character of string
      If HasIndex And ThisType = "STRING" Then
        'Check for function
        SubLoc = LocationOfSub(ValueTemp, "", "", -1)

        If SubLoc = 0 Then
          'No function, so accessing char of string - type is byte
          ThisType = "BYTE"
        Else
          ThisType = Subroutine(SubLoc)->ReturnType
        End If
      End If
    End If

    'Set FinalType to highest value
    'Print ,, ValueTemp + " is " + ThisType
    If CastOrder(ThisType) > CastOrder(FinalType) Then FinalType = ThisType

  Next

  Return FinalType
  
  End Function

Sub UpdateOutgoingCalls (CompSub As SubType Pointer)
  'Update call counts to other subroutines
  'Use after allocating a subroutine to a program memory page
  Dim As SubType Pointer CalledSub
  Dim As LinkedListElement Pointer ListItem

  'Make sure that this subroutine is on a valid page
  If CompSub->DestPage < 0 Or CompSub->DestPage > MAX_PROG_PAGES Then
    Exit Sub
  End If

  'Find all subroutines called from this one
  ListItem = CompSub->CallList->Next
  Do While ListItem <> 0
    CalledSub = ListItem->Metadata
    'Update call counts
    CalledSub->CallsFromPage(CompSub->DestPage) += ListItem->NumVal
    ListItem = ListItem->Next
  Loop
  End Sub

Sub UpdateSubMap
  'Ensure that the hash map of subroutines is up to date
  Dim As LinkedListElement Pointer SubElement
  Dim As Integer T

  'Regenerate if new subroutines have been added and old map is out of date
  If OldSBC <> SBC Then
    Subroutines = HashMapCreate
    'Copy every Sub into map
    For T = 1 To SBC
      'Add Sub name to hash map, with reference to location in array
      SubElement = HashMapGet(Subroutines, UCase(Trim(Subroutine(T)->Name)))
      If SubElement = 0 Then
        SubElement = LinkedListCreate
        HashMapSet(Subroutines, UCase(Trim(Subroutine(T)->Name)), SubElement)
        SubElement->NumVal = T
      Else
        'Name already found in hash map, add this one to list of overloads
        SubElement = LinkedListInsert(SubElement, UCase(Subroutine(T)->Name), T)
      End If

      If (( compilerdebug and cCOMPILEUPDATESUBMAP ) = cCOMPILEUPDATESUBMAP )  Then
        Print "256 CompileUpdateSubMap " + Subroutine(T)->Name
      End if

      'Add name in output code as well
      SubElement = HashMapGet(Subroutines, UCase(GetSubFullName(T)))
      If SubElement = 0 Then
        SubElement = LinkedListCreate
        HashMapSet(Subroutines, UCase(GetSubFullName(T)), SubElement)
        SubElement->NumVal = T
      End If


    Next
    OldSBC = SBC
  End If
  End Sub

Sub UpgradeCalcVar (VarName As String, VarType As String)
  'Upgrade the type of a calc var
  Dim As String Temp
  Dim As Integer CV, CD

  If VarName <> "" Then
    'If name specified, only mark that var

    'Exit if input is not a calc var
    IF Left(UCase(VarName), 7) <> "SYSTEMP" Then Exit Sub

    Temp = Mid(Trim(VarName), 8)
    CV = VAL(Temp)

    With CalcVars(CV)
      If CastOrder(.CurrentType) < CastOrder(VarType) Then
        .CurrentType = VarType
        'Also need to change max type
        If CastOrder(VarType) > CastOrder(.MaxType) Then
          .MaxType = VarType
        End If
      End If
    End With
  End If
  End Sub

Function VarAddress (ArrayNameIn As String, CurrSub As SubType Pointer) As VariableType Pointer
  Dim As String ArrayName
  Dim As Integer LO
  Dim As SubType Pointer MainSub
  Dim As VariableType Pointer FoundVar
  MainSub = Subroutine(0)

  'Tidy name
  ArrayName = ArrayNameIn
  If INSTR(ArrayNameIn, "(") <> 0 Then ArrayName = RTrim(Left(ArrayNameIn, INSTR(ArrayNameIn, "(") - 1))
  If INSTR(ArrayNameIn, "$") <> 0 Then ArrayName = RTrim(Left(ArrayNameIn, INSTR(ArrayNameIn, "$") - 1))

  'Search local variables
  FoundVar = HashMapGet(@(CurrSub->Variables), ArrayName)
  If FoundVar <> 0 Then
    Return FoundVar
  End If

  'Search global variables
  If MainSub <> CurrSub Then
    FoundVar = HashMapGet(@(MainSub->Variables), ArrayName)
    If FoundVar <> 0 Then
      Return FoundVar
    End If
  End If

  'Check if variable is a Function result
  LO = LocationOfSub(ArrayNameIn, "", "")
  '  Print ArrayNameIn, LO
  If LO > 0 Then
    With *Subroutine(LO)
      If Subroutine(LO) = 0 Then
        ' Print "Var " + ArrayName + " not found in Sub " + CurrSub->Name
        Return Cast( VariableType Pointer, INVALIDARRAYVALUE )  'DuplicateFunction name - permitting higher level methods to handle the error
      ElseIf .IsFunction Then
        'Have found function, add a var and then return it
        AddVar ArrayNameIn, .ReturnType, 1, 0, "REAL", "", , -1
        FoundVar = HashMapGet(@(MainSub->Variables), ArrayName)
        Return FoundVar
      End If
    End With
  Else
    Return Cast( VariableType Pointer, INVALIDARRAYVALUE ) 'DuplicateFunction name - permitting higher level methods to handle the error
  End If

  'Nothing found, return null pointer
  'Print "Var " + ArrayName + " not found in Sub " + CurrSub->Name
  Return 0
  END FUNCTION


Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.