/* * Copyright 2019 faddenSoft * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Diagnostics; using System.Text; using Asm65; namespace SourceGen { /// /// Analyzer attribute holder. Contains the output of the instruction and data analyzers. /// Every byte in the input file has one of these associated with it. /// /// /// (Yes, it's a mutable struct. Yes, that fact has bitten me a few times. The array /// of these may have millions of elements, so the reduction in overhead seems worthwhile.) /// public struct Anattrib { [FlagsAttribute] private enum AttribFlags { InstrStart = 1 << 0, // byte is first of an instruction Instruction = 1 << 1, // byte is part of an instruction or inline data InlineData = 1 << 2, // byte is inline data Data = 1 << 3, // byte is data EntryPoint = 1 << 8, // external code branches here BranchTarget = 1 << 9, // internal code branches here ExternalBranch = 1 << 10, // this abs/rel branch lands outside input file NoContinue = 1 << 12, // execution does not continue to following instruction Visited = 1 << 16, // has the analyzer visited this byte? Changed = 1 << 17, // set/cleared as the analyzer works Hinted = 1 << 18, // was this byte affected by a type hint? } // Flags indicating what type of data is here. Use the following Is* properties // to set/clear. private AttribFlags mAttribFlags; public bool IsInstructionStart { get { return (mAttribFlags & AttribFlags.InstrStart) != 0; } set { IsInstruction = value; if (value) { mAttribFlags |= AttribFlags.InstrStart; } else { mAttribFlags &= ~AttribFlags.InstrStart; } } } public bool IsInstruction { get { return (mAttribFlags & AttribFlags.Instruction) != 0; } set { Debug.Assert(value == false || (mAttribFlags & (AttribFlags.InlineData | AttribFlags.Data)) == 0); if (value) { mAttribFlags |= AttribFlags.Instruction; } else { mAttribFlags &= ~AttribFlags.Instruction; } } } public bool IsInlineData { get { return (mAttribFlags & AttribFlags.InlineData) != 0; } set { Debug.Assert(value == false || (mAttribFlags & (AttribFlags.Instruction | AttribFlags.Data)) == 0); if (value) { mAttribFlags |= AttribFlags.InlineData; } else { mAttribFlags &= ~AttribFlags.InlineData; } } } public bool IsData { get { return (mAttribFlags & AttribFlags.Data) != 0; } set { Debug.Assert(value == false || (mAttribFlags & (AttribFlags.InlineData | AttribFlags.Instruction)) == 0); if (value) { mAttribFlags |= AttribFlags.Data; } else { mAttribFlags &= ~AttribFlags.Data; } } } public bool IsStart { get { return IsInstructionStart || IsDataStart || IsInlineDataStart; } } public bool IsEntryPoint { get { return (mAttribFlags & AttribFlags.EntryPoint) != 0; } set { if (value) { mAttribFlags |= AttribFlags.EntryPoint; } else { mAttribFlags &= ~AttribFlags.EntryPoint; } } } public bool IsBranchTarget { get { return (mAttribFlags & AttribFlags.BranchTarget) != 0; } set { if (value) { mAttribFlags |= AttribFlags.BranchTarget; } else { mAttribFlags &= ~AttribFlags.BranchTarget; } } } public bool IsExternalBranch { get { return (mAttribFlags & AttribFlags.ExternalBranch) != 0; } set { if (value) { mAttribFlags |= AttribFlags.ExternalBranch; } else { mAttribFlags &= ~AttribFlags.ExternalBranch; } } } public bool DoesNotContinue { get { return (mAttribFlags & AttribFlags.NoContinue) != 0; } set { if (value) { mAttribFlags |= AttribFlags.NoContinue; } else { mAttribFlags &= ~AttribFlags.NoContinue; } } } public bool DoesNotBranch { get { return (BranchTaken == OpDef.BranchTaken.Never); } } public bool IsVisited { get { return (mAttribFlags & AttribFlags.Visited) != 0; } set { if (value) { mAttribFlags |= AttribFlags.Visited; } else { mAttribFlags &= ~AttribFlags.Visited; } } } public bool IsChanged { get { return (mAttribFlags & AttribFlags.Changed) != 0; } set { if (value) { mAttribFlags |= AttribFlags.Changed; } else { mAttribFlags &= ~AttribFlags.Changed; } } } public bool IsHinted { get { return (mAttribFlags & AttribFlags.Hinted) != 0; } set { if (value) { mAttribFlags |= AttribFlags.Hinted; } else { mAttribFlags &= ~AttribFlags.Hinted; } } } public bool IsDataStart { get { return IsData && DataDescriptor != null; } } public bool IsInlineDataStart { get { return IsInlineData && DataDescriptor != null; } } /// /// Get the target memory address for this byte. /// public int Address { get; set; } /// /// Instructions: length of the instruction (for InstrStart). If a FormatDescriptor is /// assigned, the length must match, or the dfd will be ignored. /// Inline data: FormatDescriptor length, or zero if no descriptor is defined. /// Data: FormatDescriptor length, or zero if no descriptor is defined. /// /// This field should only be set by CodeAnalysis methods, although the "get" value /// can be changed for data/inline-data by setting the DataDescriptor field. /// public int Length { get { // For data we don't even use the field; this ensures that we're always // using the FormatDescriptor's length. if (IsData || IsInlineData) { Debug.Assert(mLength == 0); if (DataDescriptor != null) { return DataDescriptor.Length; } else { return 0; } } return mLength; } set { Debug.Assert(!IsData); mLength = value; } } private int mLength; /// /// Instructions only: processor status flags. /// /// Note this returns a copy of a struct, so modifications to the returned value /// (including calls to Merge and Apply) are not permanent. /// public StatusFlags StatusFlags { get { return mStatusFlags; } set { mStatusFlags = value; } } private StatusFlags mStatusFlags; public void MergeStatusFlags(StatusFlags other) { mStatusFlags.Merge(other); } public void ApplyStatusFlags(StatusFlags other) { mStatusFlags.Apply(other); } /// /// Branch instructions only: outcome of branch. /// public OpDef.BranchTaken BranchTaken { get; set; } /// /// Instructions only: decoded operand address value. Will be -1 if not /// yet computed or not applicable. For a relative branch instruction, /// this will have the absolute branch target address. On the 65816, this /// will be a 24-bit address. /// public int OperandAddress { get { return mOperandAddressSet ? mOperandAddress : -1; } set { Debug.Assert(mOperandAddress >= -1); mOperandAddress = value; mOperandAddressSet = (value >= 0); } } private int mOperandAddress; private bool mOperandAddressSet; /// /// Instructions only: offset referenced by OperandAddress. Will be -1 if not /// yet computed, not applicable, or if OperandAddress refers to a location /// outside the scope of the file. /// public int OperandOffset { get { return mOperandOffsetSet ? mOperandOffset : -1; } set { Debug.Assert(mOperandOffset >= -1); mOperandOffset = value; mOperandOffsetSet = (value >= 0); } } private int mOperandOffset; private bool mOperandOffsetSet; /// /// Instructions only: is OperandOffset a direct target offset? (This is used when /// tracing jump instructions, to know if we should add the offset to the scan list. /// It's determined by the opcode, e.g. "JMP addr" -> true, "JMP (addr,X)" -> false.) /// public bool IsOperandOffsetDirect { get; set; } /// /// Symbol defined as the label for this offset. All offsets that are instruction /// or data target offsets will have one of these defined. Users can define additional /// symbols as well. /// /// Will be null if no label is defined for this offset. /// public Symbol Symbol { get; set; } /// /// Format descriptor for operands and data items. Will be null if no descriptor /// is defined for this offset. /// public FormatDescriptor DataDescriptor { get; set; } /// /// Is this an instruction with an operand (i.e. not impl/acc)? /// public bool IsInstructionWithOperand { get { if (!IsInstructionStart) { return false; } return Length != 1; } } /// /// Returns a fixed-width string with indicators for items of interest. /// public string ToAttrString() { StringBuilder sb = new StringBuilder(5); char blank = '.'; sb.Append(IsEntryPoint ? '@' : blank); sb.Append(IsHinted ? 'H' : blank); sb.Append(DoesNotBranch ? '!' : blank); sb.Append(DoesNotContinue ? '#' : blank); sb.Append(IsBranchTarget ? '>' : blank); return sb.ToString(); } public override string ToString() { StringBuilder sb = new StringBuilder(); if (IsInstruction) { sb.Append("Inst"); } else if (IsData) { sb.Append("Data"); } else if (IsInlineData) { sb.Append("Inli"); } if (IsStart) { sb.Append("Start"); } sb.Append(" len="); sb.Append(Length); return sb.ToString(); } } }