/* * Copyright 2018 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.Text; namespace Asm65 { /// /// Status flag holder. Each flag may be known to be zero, known to be one, or /// hold an indeterminate value (represented as a negative number). /// /// For the 65802/65816, we also keep track of the E flag (emulation bit), even though /// that's not actually held in the P register. /// /// Note this is a value type, not a reference type. /// /// The default value is UNSPECIFIED for all bits. /// public struct StatusFlags { private TriState16 mState; /// /// Flag bits, from processor status register definition. The 'e' (emulation) /// flag from the 65816 is tacked onto the end. /// /// The enumerated value matches the bit number in the P register. /// public enum FlagBits { C = 0, Z = 1, I = 2, D = 3, B = 4, // all CPUs except 65802/65816 in native mode X = 4, // 65802/65816 in native mode M = 5, // 65802/65816 in native mode (always 1 on other CPUs) V = 6, N = 7, E = 8 // not actually part of P-reg; accessible only through XCE } /// /// Default value (all flags UNSPECIFIED). A newly-created array of StatusFlags will /// all have this value. /// public static readonly StatusFlags DefaultValue = new StatusFlags { mState = new TriState16(0, 0) }; /// /// All flags are INDETERMINATE. /// public static readonly StatusFlags AllIndeterminate = new StatusFlags() { mState = new TriState16(0x01ff, 0x01ff) }; public int C { get { return mState.GetBit((int)FlagBits.C); } set { if (value == 0) { mState.SetZero((int)FlagBits.C); } else if (value == 1) { mState.SetOne((int)FlagBits.C); } else if (value == TriState16.UNSPECIFIED) { mState.SetUnspecified((int)FlagBits.C); } else { mState.SetIndeterminate((int)FlagBits.C); } } } public int Z { get { return mState.GetBit((int)FlagBits.Z); } set { if (value == 0) { mState.SetZero((int)FlagBits.Z); } else if (value == 1) { mState.SetOne((int)FlagBits.Z); } else if (value == TriState16.UNSPECIFIED) { mState.SetUnspecified((int)FlagBits.Z); } else { mState.SetIndeterminate((int)FlagBits.Z); } } } public int I { get { return mState.GetBit((int)FlagBits.I); } set { if (value == 0) { mState.SetZero((int)FlagBits.I); } else if (value == 1) { mState.SetOne((int)FlagBits.I); } else if (value == TriState16.UNSPECIFIED) { mState.SetUnspecified((int)FlagBits.I); } else { mState.SetIndeterminate((int)FlagBits.I); } } } public int D { get { return mState.GetBit((int)FlagBits.D); } set { if (value == 0) { mState.SetZero((int)FlagBits.D); } else if (value == 1) { mState.SetOne((int)FlagBits.D); } else if (value == TriState16.UNSPECIFIED) { mState.SetUnspecified((int)FlagBits.D); } else { mState.SetIndeterminate((int)FlagBits.D); } } } /// /// X (index register width) flag. For an unambiguous value, use IsShortX. /// public int X { get { return mState.GetBit((int)FlagBits.X); } set { if (value == 0) { mState.SetZero((int)FlagBits.X); } else if (value == 1) { mState.SetOne((int)FlagBits.X); } else if (value == TriState16.UNSPECIFIED) { mState.SetUnspecified((int)FlagBits.X); } else { mState.SetIndeterminate((int)FlagBits.X); } } } /// /// M (accumulator width) flag. For an unambiguous value, use IsShortM. /// public int M { get { return mState.GetBit((int)FlagBits.M); } set { if (value == 0) { mState.SetZero((int)FlagBits.M); } else if (value == 1) { mState.SetOne((int)FlagBits.M); } else if (value == TriState16.UNSPECIFIED) { mState.SetUnspecified((int)FlagBits.M); } else { mState.SetIndeterminate((int)FlagBits.M); } } } public int V { get { return mState.GetBit((int)FlagBits.V); } set { if (value == 0) { mState.SetZero((int)FlagBits.V); } else if (value == 1) { mState.SetOne((int)FlagBits.V); } else if (value == TriState16.UNSPECIFIED) { mState.SetUnspecified((int)FlagBits.V); } else { mState.SetIndeterminate((int)FlagBits.V); } } } public int N { get { return mState.GetBit((int)FlagBits.N); } set { if (value == 0) { mState.SetZero((int)FlagBits.N); } else if (value == 1) { mState.SetOne((int)FlagBits.N); } else if (value == TriState16.UNSPECIFIED) { mState.SetUnspecified((int)FlagBits.N); } else { mState.SetIndeterminate((int)FlagBits.N); } } } /// /// E (emulation) flag. For an unambiguous value, use IsEmulationMode. /// public int E { get { return mState.GetBit((int)FlagBits.E); } set { if (value == 0) { mState.SetZero((int)FlagBits.E); } else if (value == 1) { mState.SetOne((int)FlagBits.E); } else if (value == TriState16.UNSPECIFIED) { mState.SetUnspecified((int)FlagBits.E); } else { mState.SetIndeterminate((int)FlagBits.E); } } } public int GetBit(FlagBits index) { return mState.GetBit((int) index); } /// /// Returns true if the current processor status flags are configured for a short /// (8-bit) accumulator. /// /// /// This is (mostly) where we decide how to treat ambiguous status flags. We favor /// short flags because, when we get it wrong, it tends to be easier to spot (e.g. /// LDA #$00xx becomes LDA+BRK). Mistakenly guessing "long" also tends to result in /// instructions with other instructions embedded in them, which can be confusing. /// public bool IsShortM { get { // E==1 --> true (we're in emulation mode) // E==0 || E==? : native / assumed native // M==1 || M==? --> true (native mode, configured short or assumed short) // M==0 --> false (native mode, configured long) return (E == 1) || (M != 0); } } /// /// Returns true if the current processor status flags are configured for short /// (8-bit) X/Y registers. /// public bool IsShortX { get { // (same logic as ShortM) return (E == 1) || (X != 0); } } /// /// Returns true if the current processor status flags are configured for execution /// in native mode. /// public bool IsEmulationMode { get { // E==1 : emulation --> true // E==0 || E==? : native / assumed native --> false return E == 1; } } /// /// Access the value as a single integer. Used for serialization. /// public int AsInt { get { return mState.AsInt; } } /// /// Set the value from an integer. Used for serialization. /// public static StatusFlags FromInt(int value) { if ((value & ~0x01ff01ff) != 0) { throw new InvalidOperationException("Bad StatusFlags value " + value.ToString("x8")); } StatusFlags newFlags = new StatusFlags(); newFlags.mState.AsInt = value; return newFlags; } /// /// Merge a set of status flags into this one. /// public void Merge(StatusFlags other) { mState.Merge(other.mState); } /// /// Applies flags, overwriting existing values. This will set one or more flags /// to 0, 1, or indeterminate. Unspecified (0/0) values have no effect. /// /// This is useful when merging "overrides" in. /// public void Apply(StatusFlags overrides) { mState.Apply(overrides.mState); } /// /// Returns a string representation of the flags. /// /// If set, include the 'E' flag, and show M/X. public string ToString(bool showMXE) { StringBuilder sb = new StringBuilder(showMXE ? 10 : 8); sb.Append("-?nN"[N + 2]); sb.Append("-?vV"[V + 2]); sb.Append(showMXE ? "-?mM"[M + 2] : '-'); sb.Append(showMXE ? "-?xX"[X + 2] : '-'); sb.Append("-?dD"[D + 2]); sb.Append("-?iI"[I + 2]); sb.Append("-?zZ"[Z + 2]); sb.Append("-?cC"[C + 2]); if (showMXE) { sb.Append(' '); sb.Append("-?eE"[E + 2]); } return sb.ToString(); } public static bool operator ==(StatusFlags a, StatusFlags b) { return a.mState == b.mState; } public static bool operator !=(StatusFlags a, StatusFlags b) { return !(a == b); } public override bool Equals(object obj) { return obj is StatusFlags && this == (StatusFlags)obj; } public override int GetHashCode() { return mState.GetHashCode(); } public override string ToString() { return ToString(true); // + " [" + mState.ToString() + "]" } } }