예제 #1
0
        /// <summary>
        /// Creates an UndoableChange for a label update.
        /// </summary>
        /// <param name="offset">Affected offset.</param>
        /// <param name="oldSymbol">Current label.  May be null.</param>
        /// <param name="newSymbol">New label.  May be null.</param>
        /// <returns>Change record.</returns>
        public static UndoableChange CreateLabelChange(int offset, Symbol oldSymbol,
                                                       Symbol newSymbol)
        {
            if (oldSymbol == newSymbol)
            {
                Debug.WriteLine("No-op label change at +" + offset.ToString("x6") +
                                ": " + oldSymbol);
            }

            UndoableChange uc = new UndoableChange();

            uc.Type     = ChangeType.SetLabel;
            uc.Offset   = offset;
            uc.OldValue = oldSymbol;
            uc.NewValue = newSymbol;
            // Data analysis can change if we add or remove a label in a data area.  Label
            // selection can change as well, e.g. switching from an auto-label to a user
            // label with an adjustment.  So renaming a user-defined label doesn't require
            // reanalysis, but adding or removing one does.
            //
            // Do the reanalysis if either is empty.  This will cause an unnecessary
            // reanalysis if we change an empty label to an empty label, but that shouldn't
            // be allowed by the UI anyway.
            Debug.Assert(newSymbol == null || newSymbol.SymbolSource == Symbol.Source.User);
            if ((oldSymbol == null) || (newSymbol == null) /*||
                                                            * (oldSymbol.SymbolSource != newSymbol.SymbolSource)*/)
            {
                uc.ReanalysisRequired = ReanalysisScope.DataOnly;
            }
            else
            {
                uc.ReanalysisRequired = ReanalysisScope.None;
            }
            return(uc);
        }
예제 #2
0
        /// <summary>
        /// Creates an UndoableChange for an address map update.
        /// </summary>
        /// <param name="offset">Affected offset.</param>
        /// <param name="oldEntry">Previous address map entry, or null if none.</param>
        /// <param name="newEntry">New address map entry, or null for deletion.</param>
        /// <returns>Change record.</returns>
        public static UndoableChange CreateAddressChange(AddressMap.AddressMapEntry oldEntry,
                                                         AddressMap.AddressMapEntry newEntry)
        {
            int offset;

            if (oldEntry != null)
            {
                offset = oldEntry.Offset;
            }
            else if (newEntry != null)
            {
                offset = newEntry.Offset;
            }
            else
            {
                // Shouldn't happen.
                Debug.Assert(false);
                offset = -1;
            }
            if (oldEntry == newEntry)
            {
                Debug.WriteLine("No-op address change at +" + offset.ToString("x6"));
            }
            UndoableChange uc = new UndoableChange();

            uc.Type               = ChangeType.SetAddress;
            uc.Offset             = offset;
            uc.OldValue           = oldEntry;
            uc.NewValue           = newEntry;
            uc.ReanalysisRequired = ReanalysisScope.CodeAndData;
            return(uc);
        }
예제 #3
0
        /// <summary>
        /// Creates an UndoableChange that does nothing but force an update.
        /// </summary>
        /// <param name="flags">Desired reanalysis flags.</param>
        /// <returns>Change record.</returns>
        public static UndoableChange CreateDummyChange(ReanalysisScope flags)
        {
            UndoableChange uc = new UndoableChange();

            uc.Type               = ChangeType.Dummy;
            uc.Offset             = -1;
            uc.ReanalysisRequired = flags;
            return(uc);
        }
예제 #4
0
        /// <summary>
        /// Creates an UndoableChange for an operand or data format update.
        /// </summary>
        /// <param name="offset">Affected offset.</param>
        /// <param name="oldFormat">Current format.  May be null.</param>
        /// <param name="newFormat">New format.  May be null.</param>
        /// <returns>Change record.</returns>
        public static UndoableChange CreateOperandFormatChange(int offset,
                                                               FormatDescriptor oldFormat, FormatDescriptor newFormat)
        {
            if (oldFormat == newFormat)
            {
                Debug.WriteLine("No-op format change at +" + offset.ToString("x6") +
                                ": " + oldFormat);
            }

            // We currently allow old/new formats with different lengths.  There doesn't
            // seem to be a reason not to, and a slight performance advantage to doing so.
            // Also, if a change set has two changes at the same offset, undo requires
            // enumerating the list in reverse order.

            UndoableChange uc = new UndoableChange();

            uc.Type     = ChangeType.SetOperandFormat;
            uc.Offset   = offset;
            uc.OldValue = oldFormat;
            uc.NewValue = newFormat;

            // Data-only reanalysis is required if the old or new format has a label.  Simply
            // changing from e.g. default to decimal, or decimal to binary, doesn't matter.
            // (The format editing code ensures that labels don't appear in the middle of
            // a formatted region.)  Adding, removing, or changing a symbol can change the
            // layout of uncategorized data, affect data targets, xrefs, etc.
            //
            // We can't only check for a symbol, though, because Numeric/Address will
            // create an auto-label if the reference is within the file.
            //
            // If the number of bytes covered by the format changes, or we're adding or
            // removing a format, we need to redo the analysis of uncategorized data.  For
            // example, an auto-detected string could get larger or smaller.  We don't
            // currently have a separate flag for just that.  Also, because we're focused
            // on just one change, we can't skip reanalysis when (say) one 4-byte numeric
            // is converted to two two-byte numerics.
            if ((oldFormat != null && oldFormat.HasSymbolOrAddress) ||
                (newFormat != null && newFormat.HasSymbolOrAddress))
            {
                uc.ReanalysisRequired = ReanalysisScope.DataOnly;
            }
            else if (oldFormat == null || newFormat == null ||
                     oldFormat.Length != newFormat.Length)
            {
                uc.ReanalysisRequired = ReanalysisScope.DataOnly;
            }
            else
            {
                uc.ReanalysisRequired = ReanalysisScope.None;
            }
            return(uc);
        }
예제 #5
0
        /// <summary>
        /// Creates an UndoableChange for an address map update.
        /// </summary>
        /// <param name="offset">Affected offset.</param>
        /// <param name="oldAddress">Previous address map entry, or -1 if none.</param>
        /// <param name="newAddress">New address map entry, or -1 if none.</param>
        /// <returns>Change record.</returns>
        public static UndoableChange CreateAddressChange(int offset, int oldAddress,
                                                         int newAddress)
        {
            if (oldAddress == newAddress)
            {
                Debug.WriteLine("No-op address change at +" + offset.ToString("x6") +
                                ": " + oldAddress);
            }
            UndoableChange uc = new UndoableChange();

            uc.Type               = ChangeType.SetAddress;
            uc.Offset             = offset;
            uc.OldValue           = oldAddress;
            uc.NewValue           = newAddress;
            uc.ReanalysisRequired = ReanalysisScope.CodeAndData;
            return(uc);
        }
예제 #6
0
        /// <summary>
        /// Creates an UndoableChange for a visualization set update.
        /// </summary>
        /// <param name="offset">Affected offset.</param>
        /// <param name="oldVisSet">Old visualization set.</param>
        /// <param name="newVisSet">New visualization set.</param>
        /// <returns>Change record.</returns>
        public static UndoableChange CreateVisualizationSetChange(int offset,
                                                                  VisualizationSet oldVisSet, VisualizationSet newVisSet)
        {
            if (oldVisSet == newVisSet)
            {
                Debug.WriteLine("No-op visualization set change");
            }

            UndoableChange uc = new UndoableChange();

            uc.Type               = ChangeType.SetVisualizationSet;
            uc.Offset             = offset;
            uc.OldValue           = oldVisSet;
            uc.NewValue           = newVisSet;
            uc.ReanalysisRequired = ReanalysisScope.DisplayOnly;    // no change to code/data
            return(uc);
        }
예제 #7
0
        /// <summary>
        /// Creates an UndoableChange for a local variable table update.
        /// </summary>
        /// <param name="offset">Affected offset.</param>
        /// <param name="oldLvTable">Old table.</param>
        /// <param name="newLvTable">New table.</param>
        /// <returns>Change record.</returns>
        public static UndoableChange CreateLocalVariableTableChange(int offset,
                                                                    LocalVariableTable oldLvTable, LocalVariableTable newLvTable)
        {
            if (oldLvTable == newLvTable)
            {
                Debug.WriteLine("No-op local variable table change");
            }

            UndoableChange uc = new UndoableChange();

            uc.Type               = ChangeType.SetLocalVariableTable;
            uc.Offset             = offset;
            uc.OldValue           = oldLvTable;
            uc.NewValue           = newLvTable;
            uc.ReanalysisRequired = ReanalysisScope.DataOnly;   // update dfds in Anattribs
            return(uc);
        }
예제 #8
0
        /// <summary>
        /// Creates an UndoableChange for a note update.
        /// </summary>
        /// <param name="offset">Affected offset.</param>
        /// <param name="oldNote">Current note.</param>
        /// <param name="newNote">New note.</param>
        /// <returns>Change record.</returns>
        public static UndoableChange CreateNoteChange(int offset,
                                                      MultiLineComment oldNote, MultiLineComment newNote)
        {
            if (oldNote == newNote)
            {
                Debug.WriteLine("No-op note change at +" + offset.ToString("x6") +
                                ": " + oldNote);
            }

            UndoableChange uc = new UndoableChange();

            uc.Type               = ChangeType.SetNote;
            uc.Offset             = offset;
            uc.OldValue           = oldNote;
            uc.NewValue           = newNote;
            uc.ReanalysisRequired = ReanalysisScope.None;
            return(uc);
        }
예제 #9
0
        /// <summary>
        /// Creates an UndoableChange for a long comment update.
        /// </summary>
        /// <param name="offset">Affected offset.</param>
        /// <param name="oldComment">Current comment.</param>
        /// <param name="newComment">New comment.</param>
        /// <returns>Change record.</returns>
        public static UndoableChange CreateLongCommentChange(int offset,
                                                             MultiLineComment oldComment, MultiLineComment newComment)
        {
            if (oldComment == newComment)
            {
                Debug.WriteLine("No-op long comment change at +" + offset.ToString("x6") +
                                ": " + oldComment);
            }

            UndoableChange uc = new UndoableChange();

            uc.Type               = ChangeType.SetLongComment;
            uc.Offset             = offset;
            uc.OldValue           = oldComment;
            uc.NewValue           = newComment;
            uc.ReanalysisRequired = ReanalysisScope.None;
            return(uc);
        }
예제 #10
0
        /// <summary>
        /// Creates an UndoableChange for a status flag override update.
        /// </summary>
        /// <param name="offset">Affected offset.</param>
        /// <param name="oldFlags">Current flags.</param>
        /// <param name="newFlags">New flags.</param>
        /// <returns></returns>
        public static UndoableChange CreateStatusFlagChange(int offset, StatusFlags oldFlags,
                                                            StatusFlags newFlags)
        {
            if (oldFlags == newFlags)
            {
                Debug.WriteLine("No-op status flag change at " + offset);
            }
            UndoableChange uc = new UndoableChange();

            uc.Type     = ChangeType.SetStatusFlagOverride;
            uc.Offset   = offset;
            uc.OldValue = oldFlags;
            uc.NewValue = newFlags;
            // This can affect instruction widths (for M/X) and conditional branches.  We
            // don't need to re-analyze for changes to I/D, but users don't really need to
            // change those anyway, so it's not worth optimizing.
            uc.ReanalysisRequired = ReanalysisScope.CodeAndData;
            return(uc);
        }
예제 #11
0
        /// <summary>
        /// Creates an UndoableChange for a data bank register update.
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="oldValue"></param>
        /// <param name="newValue"></param>
        /// <returns></returns>
        public static UndoableChange CreateDataBankChange(int offset,
                                                          CodeAnalysis.DbrValue oldValue, CodeAnalysis.DbrValue newValue)
        {
            if (oldValue == newValue)
            {
                Debug.WriteLine("No-op DBR change at +" + offset.ToString("x6") + ": " + oldValue);
            }
            UndoableChange uc = new UndoableChange();

            uc.Type     = ChangeType.SetDataBank;
            uc.Offset   = offset;
            uc.OldValue = oldValue;
            uc.NewValue = newValue;
            // We don't strictly need to re-analyze the code, since the current implementation
            // handles it as a post-analysis fixup, but this lets us avoid having to compute the
            // affected offsets.
            uc.ReanalysisRequired = ReanalysisScope.CodeAndData;
            return(uc);
        }
예제 #12
0
        /// <summary>
        /// Creates an UndoableChange for a type hint update.  Rather than adding a
        /// separate UndoableChange for each affected offset -- which could span the
        /// entire file -- we use range sets to record the before/after state.
        /// </summary>
        /// <param name="undoSet">Current values.</param>
        /// <param name="newSet">New values.</param>
        /// <returns>Change record.</returns>
        public static UndoableChange CreateTypeHintChange(TypedRangeSet undoSet,
                                                          TypedRangeSet newSet)
        {
            if (newSet.Count == 0)
            {
                Debug.WriteLine("Empty hint change?");
            }
            UndoableChange uc = new UndoableChange();

            uc.Type     = ChangeType.SetTypeHint;
            uc.Offset   = -1;
            uc.OldValue = undoSet;
            uc.NewValue = newSet;
            // Any hint change can affect whether something is treated as code.
            // Either we're deliberately setting it as code or non-code, or we're
            // setting it to "no hint", which means the code analyzer gets
            // to make the decision now.  This requires a full code+data re-analysis.
            uc.ReanalysisRequired = ReanalysisScope.CodeAndData;
            return(uc);
        }
예제 #13
0
        /// <summary>
        /// Creates an UndoableChange for a change to the project properties.
        /// </summary>
        /// <param name="oldNote">Current note.</param>
        /// <param name="newNote">New note.</param>
        /// <returns>Change record.</returns>
        public static UndoableChange CreateProjectPropertiesChange(ProjectProperties oldProps,
                                                                   ProjectProperties newProps)
        {
            Debug.Assert(oldProps != null && newProps != null);
            if (oldProps == newProps)   // doesn't currently work except as reference check
            {
                Debug.WriteLine("No-op property change: " + oldProps);
            }

            UndoableChange uc = new UndoableChange();

            uc.Type     = ChangeType.SetProjectProperties;
            uc.Offset   = -1;
            uc.OldValue = oldProps;
            uc.NewValue = newProps;

            // Project properties could change the CPU type, requiring a full code+data
            // reanalysis.  We could scan the objects to see what actually changed, but that
            // doesn't seem worthwhile.
            uc.ReanalysisRequired = ReanalysisScope.CodeAndData;
            return(uc);
        }
예제 #14
0
        /// <summary>
        /// Import comments in SGEC format.
        /// </summary>
        /// <param name="pathName">File to read from.</param>
        /// <param name="proj">Project object.</param>
        /// <param name="cs">Change set that will hold changes.</param>
        /// <param name="detailMsg">Failure detail, or null on success.</param>
        /// <returns>True on success.</returns>
        public static bool ImportFromFile(string pathName, DisasmProject proj, ChangeSet cs,
                                          out string detailMsg)
        {
            string[] lines;
            try {
                lines = File.ReadAllLines(pathName);
            } catch (IOException ex) {
                // not expecting this to happen
                detailMsg = ex.Message;
                return(false);
            }

            JavaScriptSerializer ser = new JavaScriptSerializer();

            int lineNum    = 0;
            int prevOffset = -1;

            foreach (string line in lines)
            {
                lineNum++;      // first line is 1
                if (string.IsNullOrEmpty(line) || line[0] == '#')
                {
                    // ignore
                    continue;
                }
                MatchCollection matches = sLineRegex.Matches(line);
                if (matches.Count != 1)
                {
                    detailMsg = "Line " + lineNum + ": unable to parse into tokens";
                    return(false);
                }

                string posStr = matches[0].Groups[GROUP_POS].Value;
                int    offset;
                if (posStr[0] == '+')
                {
                    // offset
                    if (!Asm65.Number.TryParseIntHex(posStr.Substring(1), out offset))
                    {
                        detailMsg = "Line " + lineNum + ": unable to parse offset '" +
                                    posStr + "'";
                        return(false);
                    }
                }
                else if (posStr[0] == '$')
                {
                    // address
                    if (!Asm65.Address.ParseAddress(posStr, (1 << 24) - 1, out int addr))
                    {
                        detailMsg = "Line " + lineNum + ": unable to parse address '" +
                                    posStr + "'";
                        return(false);
                    }
                    offset = proj.AddrMap.AddressToOffset(0, addr);
                }
                else if (posStr[0] == '>')
                {
                    // relative offset
                    if (prevOffset < 0)
                    {
                        detailMsg = "Line " + lineNum + ": first address/offset cannot be relative";
                        return(false);
                    }
                    if (!Asm65.Number.TryParseInt(posStr.Substring(1), out int delta, out int _))
                    {
                        detailMsg = "Line " + lineNum + ": unable to parse delta";
                        return(false);
                    }
                    offset = prevOffset + delta;
                }
                else
                {
                    detailMsg = "Line " + lineNum + ": unknown position type '" + posStr[0] + "'";
                    return(false);
                }

                prevOffset = offset;

                if (!proj.GetAnattrib(offset).IsStart)
                {
                    // This causes problems when we try to do a LineListGen update, because
                    // we specifically request it to do the modified offset, which happens to
                    // be in the middle of an instruction, and it gets very confused.
                    detailMsg = "Line " + lineNum + ": attempt to modify middle of instr/data item";
                    return(false);
                }

                string cmdStr   = matches[0].Groups[GROUP_CMD].Value;
                string valueStr = matches[0].Groups[GROUP_VALUE].Value;
                switch (cmdStr)
                {
                case SET_COMMENT: {
                    string oldComment = proj.Comments[offset];
                    string newComment = valueStr;
                    if (oldComment == newComment)
                    {
                        // no change
                        break;
                    }
                    if (!string.IsNullOrEmpty(oldComment))
                    {
                        // overwriting existing entry; make a note
                        Debug.WriteLine("Replacing comment +" + offset.ToString("x6") +
                                        " '" + oldComment + "'");
                    }
                    UndoableChange uc = UndoableChange.CreateCommentChange(offset,
                                                                           oldComment, newComment);
                    cs.Add(uc);
                }
                break;

                case SET_LONG_COMMENT: {
                    if (!DeserializeMlc(ser, valueStr, false,
                                        out MultiLineComment newComment))
                    {
                        detailMsg = "Line " + lineNum + ": failed to deserialize value";
                        return(false);
                    }
                    proj.LongComments.TryGetValue(offset, out MultiLineComment oldComment);
                    if (oldComment == newComment)
                    {
                        // no change
                        break;
                    }
                    UndoableChange uc = UndoableChange.CreateLongCommentChange(offset,
                                                                               oldComment, newComment);
                    cs.Add(uc);
                }
                break;

                case SET_NOTE: {
                    if (!DeserializeMlc(ser, valueStr, true,
                                        out MultiLineComment newNote))
                    {
                        detailMsg = "Line " + lineNum + ": failed to deserialize value";
                        return(false);
                    }
                    proj.Notes.TryGetValue(offset, out MultiLineComment oldNote);
                    if (oldNote == newNote)
                    {
                        // no change
                        break;
                    }
                    UndoableChange uc = UndoableChange.CreateNoteChange(offset,
                                                                        oldNote, newNote);
                    cs.Add(uc);
                }
                break;

                default:
                    detailMsg = "Line " + lineNum + ": unknown command '" + cmdStr + "'";
                    return(false);
                }
            }

            detailMsg = "applied " + cs.Count + " changes.";
            return(true);
        }