示例#1
0
        /// <summary>
        /// Creates one or more FormatDescriptor entries for the specified range, adding them
        /// to the Results list.
        ///
        /// This will either create one entry that spans the entire range (for e.g. strings
        /// and bulk data), or create equal-sized chunks.
        /// </summary>
        /// <param name="type">Region data type.</param>
        /// <param name="subType">Region data sub-type.</param>
        /// <param name="chunkLength">Length of a chunk, or -1 for full buffer.</param>
        /// <param name="symbolRef">Symbol reference, or null if not applicable.</param>
        /// <param name="low">Offset of first byte in range.</param>
        /// <param name="high">Offset of last byte in range.</param>
        private void CreateSimpleEntries(FormatDescriptor.Type type,
                                         FormatDescriptor.SubType subType, int chunkLength,
                                         WeakSymbolRef symbolRef, int low, int high)
        {
            if (chunkLength == -1)
            {
                chunkLength = (high - low) + 1;
            }
            Debug.Assert(((high - low + 1) / chunkLength) * chunkLength == high - low + 1);

            // Either we have one chunk, or we have multiple chunks with the same type and
            // length.  Either way, we only need to create the descriptor once.  (This is
            // safe because FormatDescriptor instances are immutable.)
            //
            // Because certain details, like the fill byte and high-vs-low ASCII, are pulled
            // out of the data stream at format time, we don't have to dig for them now.
            FormatDescriptor dfd;

            if (subType == FormatDescriptor.SubType.Symbol)
            {
                dfd = FormatDescriptor.Create(chunkLength, symbolRef,
                                              type == FormatDescriptor.Type.NumericBE);
            }
            else
            {
                dfd = FormatDescriptor.Create(chunkLength, type, subType);
            }

            while (low <= high)
            {
                Results.Add(low, dfd);
                low += chunkLength;
            }
        }
示例#2
0
        /// <summary>
        /// Creates one or more FormatDescriptor entries for the specified range, adding them
        /// to the Results list.
        /// </summary>
        /// <param name="low">Offset of first byte in range.</param>
        /// <param name="high">Offset of last byte in range.</param>
        /// <param name="subType">String sub-type.</param>
        private void CreateLengthStringEntries(int low, int high,
                                               FormatDescriptor.SubType subType)
        {
            int i;

            for (i = low; i <= high;)
            {
                int length = mFileData[i];
                if (subType == FormatDescriptor.SubType.L16String)
                {
                    length |= mFileData[i + 1] << 8;
                    length += 2;
                }
                else
                {
                    length++;
                }
                // Zero-length strings are allowed.
                FormatDescriptor dfd = FormatDescriptor.Create(length,
                                                               FormatDescriptor.Type.String, subType);
                Results.Add(i, dfd);
                i += length;
            }

            Debug.Assert(i == high + 1);
        }
示例#3
0
        /// <summary>
        /// Creates a format descriptor for a single-byte numeric value.
        /// </summary>
        /// <param name="offset">File offset.</param>
        /// <param name="subType">How to format the item.</param>
        private void CreateByteFD(int offset, FormatDescriptor.SubType subType)
        {
            FormatDescriptor dfd = FormatDescriptor.Create(1,
                                                           FormatDescriptor.Type.NumericLE, subType);

            Results.Add(offset, dfd);
        }
示例#4
0
        // IGenerator
        public string ModifyOpcode(int offset, OpDef op)
        {
            if (op.IsUndocumented)
            {
                return(null);
            }

            // The assembler works correctly if the symbol is defined as a two-digit hex
            // value (e.g. "foo equ $80") but fails if it's four (e.g. "foo equ $0080").  We
            // output symbols with minimal digits, but we have no control over labels when
            // the code has a zero-page EQU.  So if the operand is a reference to a user
            // label, we need to output the instruction as hex.
            if (op == OpDef.OpPEI_StackDPInd ||
                op == OpDef.OpSTY_DPIndexX ||
                op == OpDef.OpSTX_DPIndexY ||
                op.AddrMode == OpDef.AddressMode.DPIndLong ||
                op.AddrMode == OpDef.AddressMode.DPInd ||
                op.AddrMode == OpDef.AddressMode.DPIndexXInd)
            {
                FormatDescriptor dfd = Project.GetAnattrib(offset).DataDescriptor;
                if (dfd != null && dfd.HasSymbol)
                {
                    // It has a symbol.  See if the symbol target is a label (auto or user).
                    if (Project.SymbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym))
                    {
                        if (sym.IsInternalLabel)
                        {
                            return(null);
                        }
                    }
                }
            }

            return(string.Empty);
        }
示例#5
0
        private void okButton_Click(object sender, EventArgs e)
        {
            bool isConstant = constantRadioButton.Checked;

            ParseValue(out int value, out int numBase);
            FormatDescriptor.SubType subType = FormatDescriptor.GetSubTypeForBase(numBase);
            DefSym = new DefSymbol(labelTextBox.Text, value, Symbol.Source.Project,
                                   isConstant ? Symbol.Type.Constant : Symbol.Type.ExternalAddr,
                                   subType, commentTextBox.Text, string.Empty);
        }
示例#6
0
        private void OnFilterChanged(object o, GLib.NotifyArgs args)
        {
            FileChooserDialog fcd = (FileChooserDialog)o;

            // find the FormatDescriptor
            FormatDescriptor format_desc = PintaCore.System.ImageFormats.Formats.Single(f => f.Filter == fcd.Filter);

            // adjust the filename
            var p = fcd.Filename;

            p = Path.ChangeExtension(Path.GetFileName(p), format_desc.Extensions[0]);
            fcd.CurrentName = p;
        }
示例#7
0
        // IGenerator
        public FormatDescriptor ModifyInstructionOperandFormat(int offset, FormatDescriptor dfd,
                                                               int operand)
        {
            if (dfd.FormatType == FormatDescriptor.Type.NumericLE && dfd.IsStringOrCharacter &&
                (operand & 0x7f) == (byte)',')
            {
                // Merlin throws an error on comma operands, e.g. LDA #','
                dfd = FormatDescriptor.Create(dfd.Length,
                                              FormatDescriptor.Type.NumericLE, FormatDescriptor.SubType.None);
            }

            return(dfd);
        }
示例#8
0
        /// <summary>
        /// Determines the offset of the label that the operand's symbol references.
        /// </summary>
        /// <param name="gen">Source generator reference.</param>
        /// <param name="offset">Offset of instruction opcode.</param>
        /// <returns>The offset of the label, or -1 if the operand isn't a symbolic reference
        ///   to a known label.</returns>
        private static int GetLabelOffsetFromOperand(IGenerator gen, int offset)
        {
            DisasmProject proj = gen.Project;

            Debug.Assert(proj.GetAnattrib(offset).IsInstructionStart);

            FormatDescriptor dfd = proj.GetAnattrib(offset).DataDescriptor;

            if (dfd == null || !dfd.HasSymbol)
            {
                return(-1);
            }
            return(proj.FindLabelOffsetByName(dfd.SymbolRef.Label));
        }
示例#9
0
        // IGenerator
        public FormatDescriptor ModifyInstructionOperandFormat(int offset, FormatDescriptor dfd,
                                                               int operand)
        {
            string badChars = ",{}";

            if (dfd.FormatType == FormatDescriptor.Type.NumericLE && dfd.IsStringOrCharacter &&
                badChars.IndexOf((char)(operand & 0x7f)) >= 0)
            {
                // Merlin throws an error on certain ASCII operands, e.g. LDA #','
                dfd = FormatDescriptor.Create(dfd.Length,
                                              FormatDescriptor.Type.NumericLE, FormatDescriptor.SubType.None);
            }

            return(dfd);
        }
示例#10
0
        // IGenerator
        public void UpdateCharacterEncoding(FormatDescriptor dfd)
        {
            CharEncoding.Encoding newEnc = PseudoOp.SubTypeToEnc(dfd.FormatSubType);
            if (newEnc == CharEncoding.Encoding.Unknown)
            {
                // probably not a character operand
                return;
            }
            if (newEnc != mCurrentEncoding)
            {
                switch (newEnc)
                {
                case CharEncoding.Encoding.Ascii:
                    OutputLine(string.Empty, ".enc", '"' + ASCII_ENC_NAME + '"', string.Empty);
                    break;

                case CharEncoding.Encoding.HighAscii:
                    // If this is a numeric operand (not string), and we're currently in
                    // ASCII mode, the "| $80" in the delimiter will handle this without
                    // the need for a .enc.  Much less clutter for sources that have plain
                    // ASCII strings but test high ASCII constants.
                    if (mCurrentEncoding == CharEncoding.Encoding.Ascii && !dfd.IsString)
                    {
                        newEnc = mCurrentEncoding;
                    }
                    else
                    {
                        OutputLine(string.Empty, ".enc", '"' + HIGH_ASCII_ENC_NAME + '"',
                                   string.Empty);
                    }
                    break;

                case CharEncoding.Encoding.C64Petscii:
                    OutputLine(string.Empty, ".enc", "\"none\"", string.Empty);
                    break;

                case CharEncoding.Encoding.C64ScreenCode:
                    OutputLine(string.Empty, ".enc", "\"screen\"", string.Empty);
                    break;

                default:
                    Debug.Assert(false);
                    break;
                }
                mCurrentEncoding = newEnc;
            }
        }
示例#11
0
        private void OkButton_Click(object sender, RoutedEventArgs e)
        {
            ParseValue(out int value, out int numBase);
            FormatDescriptor.SubType subType = FormatDescriptor.GetSubTypeForBase(numBase);
            int width = -1;

            if (IsConstant && !IsVariable)
            {
                // width field is ignored, don't bother parsing
            }
            else if (!string.IsNullOrEmpty(VarWidth))
            {
                bool ok = Asm65.Number.TryParseInt(VarWidth, out width, out int unusedNumBase);
                Debug.Assert(ok);
            }

            DefSymbol.DirectionFlags direction;
            if (IsReadChecked && IsWriteChecked)
            {
                direction = DefSymbol.DirectionFlags.ReadWrite;
            }
            else if (IsReadChecked)
            {
                direction = DefSymbol.DirectionFlags.Read;
            }
            else if (IsWriteChecked)
            {
                direction = DefSymbol.DirectionFlags.Write;
            }
            else
            {
                Debug.Assert(false);
                direction = DefSymbol.DirectionFlags.None;
            }

            // Parse and strip the annotation.
            string trimLabel = Symbol.TrimAndValidateLabel(Label, string.Empty, out bool unused1,
                                                           out bool unused2, out bool unused3, out bool unused4,
                                                           out Symbol.LabelAnnotation anno);

            NewSym = new DefSymbol(trimLabel, value,
                                   IsVariable ? Symbol.Source.Variable : Symbol.Source.Project,
                                   IsConstant ? Symbol.Type.Constant : Symbol.Type.ExternalAddr, anno,
                                   subType, width, width > 0, Comment, direction, null, string.Empty);

            DialogResult = true;
        }
示例#12
0
 /// <summary>
 /// Creates a format descriptor for ASCII data.  If the data is only one byte long,
 /// a single-byte ASCII char item is emitted instead.
 /// </summary>
 /// <param name="offset">Offset of first byte.</param>
 /// <param name="length">Length of string.</param>
 /// <param name="subType">String sub-type.</param>
 private void CreateStringOrByte(int offset, int length,
                                 FormatDescriptor.SubType subType)
 {
     Debug.Assert(length > 0);
     if (length == 1)
     {
         // single byte, output as single ASCII char rather than 1-byte string
         CreateByteFD(offset, FormatDescriptor.SubType.Ascii);
     }
     else
     {
         FormatDescriptor dfd;
         dfd = FormatDescriptor.Create(length,
                                       FormatDescriptor.Type.String, subType);
         Results.Add(offset, dfd);
     }
 }
示例#13
0
        private void CheckAsciiFormats(out bool hasAscii, out bool hasHighAscii)
        {
            int offset = 0;

            hasAscii = hasHighAscii = false;
            while (offset < Project.FileData.Length)
            {
                Anattrib         attr = Project.GetAnattrib(offset);
                FormatDescriptor dfd  = attr.DataDescriptor;
                if (dfd != null)
                {
                    if (dfd.FormatSubType == FormatDescriptor.SubType.Ascii)
                    {
                        Debug.Assert(dfd.IsNumeric || dfd.IsString);
                        hasAscii = true;
                    }
                    else if (dfd.FormatSubType == FormatDescriptor.SubType.HighAscii)
                    {
                        hasHighAscii = true;
                    }
                }
                if (hasAscii && hasHighAscii)
                {
                    return;
                }

                if (attr.IsInstructionStart)
                {
                    // look for embedded instructions, which might have formatted char data
                    int len;
                    for (len = 1; len < attr.Length; len++)
                    {
                        if (Project.GetAnattrib(offset + len).IsInstructionStart)
                        {
                            break;
                        }
                    }
                    offset += len;
                }
                else
                {
                    // data items
                    offset += attr.Length;
                }
            }
        }
示例#14
0
        private bool SaveFile(Document document, string file, FormatDescriptor format)
        {
            if (string.IsNullOrEmpty(file))
            {
                file = document.PathAndFileName;
            }

            if (format == null)
            {
                format = PintaCore.System.ImageFormats.GetFormatByFile(file);
            }

            if (format == null || format.IsReadOnly())
            {
                MessageDialog md = new MessageDialog(PintaCore.Chrome.MainWindow, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, Catalog.GetString("Pinta does not support saving images in this file format."), file);
                md.Title = Catalog.GetString("Error");

                md.Run();
                md.Destroy();
                return(false);
            }

            // If the user tries to save over a read only file, give a more informative error message than "Unhandled Exception"
            FileInfo file_info = new FileInfo(file);

            if (file_info.Exists && file_info.IsReadOnly)
            {
                MessageDialog md = new MessageDialog(PintaCore.Chrome.MainWindow, DialogFlags.Modal, MessageType.Error,
                                                     ButtonsType.Ok, Catalog.GetString("Cannot save read only file."));
                md.Title = Catalog.GetString("Error");

                md.Run();
                md.Destroy();
                return(false);
            }

            // Commit any pending changes
            PintaCore.Tools.Commit();

            format.Exporter.Export(document, file);

            document.Filename = Path.GetFileName(file);
            document.IsDirty  = false;

            return(true);
        }
示例#15
0
        /// <summary>
        /// Creates one or more FormatDescriptor entries for the specified range, adding them
        /// to the Results list.
        /// </summary>
        /// <param name="low">Offset of first byte in range.</param>
        /// <param name="high">Offset of last byte in range.</param>
        /// <param name="subType">String sub-type.</param>
        private void CreateDciStringEntries(int low, int high,
                                            FormatDescriptor.SubType subType)
        {
            int start, end, adj, endMask;

            if (subType == FormatDescriptor.SubType.Dci)
            {
                start = low;
                end   = high + 1;
                adj   = 1;
            }
            else if (subType == FormatDescriptor.SubType.DciReverse)
            {
                start = high;
                end   = low - 1;
                adj   = -1;
            }
            else
            {
                Debug.Assert(false);
                return;
            }

            // Zero-length strings aren't a thing for DCI.  The analyzer requires that all
            // strings in a region have the same polarity, so just grab the last byte.
            endMask = mFileData[end - 1] & 0x80;

            int stringStart = start;

            for (int i = start; i != end; i += adj)
            {
                byte val = mFileData[i];
                if ((val & 0x80) == endMask)
                {
                    // found the end of a string
                    int length           = (i - stringStart) * adj + 1;
                    FormatDescriptor dfd = FormatDescriptor.Create(length,
                                                                   FormatDescriptor.Type.String, subType);
                    Results.Add(stringStart < i ? stringStart : i, dfd);
                    stringStart = i + adj;
                }
            }

            Debug.Assert(stringStart == end);
        }
示例#16
0
        // IGenerator
        public string ModifyOpcode(int offset, OpDef op)
        {
            if (op.IsUndocumented)
            {
                return(null);
            }
            if (Project.CpuDef.Type == CpuDef.CpuType.CpuW65C02)
            {
                if ((op.Opcode & 0x0f) == 0x07 || (op.Opcode & 0x0f) == 0x0f)
                {
                    // BBR, BBS, RMB, SMB not supported
                    return(null);
                }
            }

            // The assembler works correctly if the symbol is defined as a two-digit hex
            // value (e.g. "foo equ $80") but fails if it's four (e.g. "foo equ $0080").  We
            // output symbols with minimal digits, but this doesn't help if the code itself
            // lives on zero page.  If the operand is a reference to a zero-page user label,
            // we need to output the instruction as hex.
            // More info: https://github.com/apple2accumulator/merlin32/issues/8
            if (op == OpDef.OpPEI_StackDPInd ||
                op == OpDef.OpSTY_DPIndexX ||
                op == OpDef.OpSTX_DPIndexY ||
                op.AddrMode == OpDef.AddressMode.DPIndLong ||
                op.AddrMode == OpDef.AddressMode.DPInd ||
                op.AddrMode == OpDef.AddressMode.DPIndexXInd)
            {
                FormatDescriptor dfd = Project.GetAnattrib(offset).DataDescriptor;
                if (dfd != null && dfd.HasSymbol)
                {
                    // It has a symbol.  See if the symbol target is a label (auto or user).
                    if (Project.SymbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym))
                    {
                        if (sym.IsInternalLabel)
                        {
                            return(null);
                        }
                    }
                }
            }

            return(string.Empty);
        }
        private void OnFilterChanged(object o, GLib.NotifyArgs args)
        {
            FileChooserDialog fcd = (FileChooserDialog)o;

            // Ensure that the file filter is never blank.
            if (fcd.Filter == null)
            {
                fcd.Filter = PintaCore.System.ImageFormats.GetDefaultSaveFormat().Filter;
                return;
            }

            // find the FormatDescriptor
            FormatDescriptor format_desc = PintaCore.System.ImageFormats.Formats.Single(f => f.Filter == fcd.Filter);

            // adjust the filename
            var p = fcd.Filename;

            p = Path.ChangeExtension(Path.GetFileName(p), format_desc.Extensions[0]);
            fcd.CurrentName = p;
        }
示例#18
0
        /// <summary>
        /// Determines whether the instruction at the specified offset has an operand that is
        /// a forward reference.  This only matters for single-pass assemblers.
        /// </summary>
        /// <param name="gen">Source generator reference.</param>
        /// <param name="offset">Offset of instruction opcode.</param>
        /// <returns>True if the instruction's operand is a forward reference to a label.</returns>
        private static bool IsForwardLabelReference(IGenerator gen, int offset)
        {
            DisasmProject proj = gen.Project;

            Debug.Assert(proj.GetAnattrib(offset).IsInstructionStart);

            FormatDescriptor dfd = proj.GetAnattrib(offset).DataDescriptor;

            if (dfd == null || !dfd.HasSymbol)
            {
                return(false);
            }
            int labelOffset = proj.FindLabelOffsetByName(dfd.SymbolRef.Label);

            if (labelOffset <= offset)
            {
                // Doesn't exist, or is backward reference.
                return(false);
            }
            return(true);
        }
示例#19
0
        /// <summary>
        /// Checks to see if the junk alignment directive is compatible with the actual
        /// address.  This is used to screen out alignment values that no longer match up
        /// with the actual addresses.
        /// </summary>
        /// <param name="offset">File offset of directive.</param>
        /// <param name="dfd">Format descriptor.</param>
        /// <param name="addrMap">Offset to address map.</param>
        /// <returns>True if the .junk alignment directive is correct, false if it's
        ///   incorrect or not an alignment sub-type (e.g. None).</returns>
        public static bool CheckJunkAlign(int offset, FormatDescriptor dfd,
                                          CommonUtil.AddressMap addrMap)
        {
            Debug.Assert(dfd.FormatType == FormatDescriptor.Type.Junk);
            if (dfd.FormatSubType == FormatDescriptor.SubType.None)
            {
                return(false);
            }
            Debug.Assert(dfd.IsAlignedJunk);

            // Just check the address.  Shouldn't need to check the length.
            int  lastOffset  = offset + dfd.Length - 1;
            int  alignToAddr = addrMap.OffsetToAddress(lastOffset) + 1;
            int  alignPwr    = FormatDescriptor.AlignmentToPower(dfd.FormatSubType);
            int  alignMask   = (1 << alignPwr) - 1;
            bool result      = (alignToAddr & alignMask) == 0;

            //Debug.WriteLine(dfd.FormatSubType + " at +" + offset.ToString("x6") +
            //    "(" + alignToAddr.ToString("x4") + "): " + result);
            return(result);
        }
示例#20
0
        /// <summary>
        /// Creates one or more FormatDescriptor entries for the specified range, adding them
        /// to the Results list.
        /// </summary>
        /// <param name="low">Offset of first byte in range.</param>
        /// <param name="high">Offset of last byte in range.</param>
        /// <param name="subType">String sub-type.</param>
        private void CreateCStringEntries(int low, int high,
                                          FormatDescriptor.SubType subType)
        {
            int startOffset = low;

            for (int i = low; i <= high; i++)
            {
                if (mFileData[i] == 0x00)
                {
                    // End of string.  Zero-length strings are allowed.
                    FormatDescriptor dfd = FormatDescriptor.Create(
                        i - startOffset + 1, FormatDescriptor.Type.String, subType);
                    Results.Add(startOffset, dfd);
                    startOffset = i + 1;
                }
                else
                {
                    // keep going
                }
            }

            // Earlier analysis guaranteed that the last byte in the buffer is 0x00.
            Debug.Assert(startOffset == high + 1);
        }
示例#21
0
 /// <summary>
 /// Method for formating string with descriptor
 /// </summary>
 /// <param name="value">source string</param>
 /// <param name="desc">format descriptor</param>
 /// <returns>string after formating</returns>
 public static string Format(string value, FormatDescriptor desc)
 {
     if (desc == FormatDescriptor.AllLowercase)
     {
         return(value.ToLowerInvariant());
     }
     else if (desc == FormatDescriptor.AllUppercase)
     {
         return(value.ToUpperInvariant());
     }
     else if (desc == FormatDescriptor.FirstUppercase)
     {
         var nvalue = value.ToLowerInvariant();
         nvalue = nvalue[0].ToString().ToUpperInvariant() + nvalue.Substring(1);
         return(nvalue);
     }
     else if (desc == FormatDescriptor.FirstLowercase)
     {
         var nvalue = value.ToUpperInvariant();
         nvalue = nvalue[0].ToString().ToLowerInvariant() + nvalue.Substring(1);
         return(nvalue);
     }
     return(value);
 }
        private bool SaveFile(Document document, string file, FormatDescriptor format, Window parent)
        {
            if (string.IsNullOrEmpty(file))
            {
                file = document.PathAndFileName;
            }

            if (format == null)
            {
                format = PintaCore.System.ImageFormats.GetFormatByFile(file);
            }

            if (format == null || format.IsReadOnly())
            {
                MessageDialog md = new MessageDialog(parent, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, Catalog.GetString("Pinta does not support saving images in this file format."), file);
                md.Title = Catalog.GetString("Error");

                md.Run();
                md.Destroy();
                return(false);
            }

            // If the user tries to save over a read only file, give a more informative error message than "Unhandled Exception"
            FileInfo file_info = new FileInfo(file);

            if (file_info.Exists && file_info.IsReadOnly)
            {
                MessageDialog md = new MessageDialog(parent, DialogFlags.Modal, MessageType.Error,
                                                     ButtonsType.Ok, Catalog.GetString("Cannot save read only file."));
                md.Title = Catalog.GetString("Error");

                md.Run();
                md.Destroy();
                return(false);
            }

            // Commit any pending changes
            PintaCore.Tools.Commit();

            try {
                format.Exporter.Export(document, file, parent);
            } catch (GLib.GException e) {             // Errors from GDK
                if (e.Message == "Image too large to be saved as ICO")
                {
                    string primary   = Catalog.GetString("Image too large");
                    string secondary = Catalog.GetString("ICO files can not be larger than 255 x 255 pixels.");
                    string message   = string.Format(markup, primary, secondary);

                    MessageDialog md = new MessageDialog(parent, DialogFlags.Modal, MessageType.Error,
                                                         ButtonsType.Ok, message);

                    md.Run();
                    md.Destroy();
                    return(false);
                }
                else
                {
                    throw e;                     // Only catch exceptions we know the reason for
                }
            } catch (OperationCanceledException) {
                return(false);
            }

            // Set Pathname and Filename properties which triggers the document.Renamed event
            document.PathAndFileName = file;

            PintaCore.Tools.CurrentTool.DoAfterSave();

            // Mark the document as clean following the tool's after-save handler, which might
            // adjust history (e.g. undo changes that were committed before saving).
            document.Workspace.History.SetClean();

            //Now the Document has been saved to the file it's associated with in this session.
            document.HasBeenSavedInSession = true;

            return(true);
        }
示例#23
0
        private void OutputString(int offset, string labelStr, string commentStr)
        {
            // Generic strings whose encoding matches the configured text encoding are output
            // with a simple .text directive.
            //
            // CString and L8String have directives (.null, .ptext), but we can only use
            // them if the string fits on one line and doesn't include delimiters.
            //
            // We might be able to define a macro for Reverse.
            //
            // We don't currently switch character encodings in the middle of a file.  We could
            // do so to flip between PETSCII, screen codes, low ASCII, and high ASCII, but it
            // adds a lot of noise and it's unclear that this is generally useful.

            Anattrib         attr = Project.GetAnattrib(offset);
            FormatDescriptor dfd  = attr.DataDescriptor;

            Debug.Assert(dfd != null);
            Debug.Assert(dfd.IsString);
            Debug.Assert(dfd.Length > 0);

            CharEncoding.Convert charConv = null;
            CharEncoding.Convert dciConv  = null;
            switch (dfd.FormatSubType)
            {
            case FormatDescriptor.SubType.Ascii:
                charConv = CharEncoding.ConvertAscii;
                dciConv  = CharEncoding.ConvertLowAndHighAscii;
                break;

            case FormatDescriptor.SubType.HighAscii:
                charConv = CharEncoding.ConvertHighAscii;
                dciConv  = CharEncoding.ConvertLowAndHighAscii;
                break;

            case FormatDescriptor.SubType.C64Petscii:
                charConv = CharEncoding.ConvertC64Petscii;
                dciConv  = CharEncoding.ConvertLowAndHighC64Petscii;
                break;

            case FormatDescriptor.SubType.C64Screen:
                charConv = CharEncoding.ConvertC64ScreenCode;
                dciConv  = CharEncoding.ConvertLowAndHighC64ScreenCode;
                break;

            default:
                break;
            }
            if (charConv == null)
            {
                OutputNoJoy(offset, dfd.Length, labelStr, commentStr);
                return;
            }

            // Issue a .enc, if needed.
            UpdateCharacterEncoding(dfd);

            Formatter formatter = SourceFormatter;

            byte[] data = Project.FileData;
            int    hiddenLeadingBytes = 0;
            int    shownLeadingBytes  = 0;
            int    trailingBytes      = 0;
            string opcodeStr;

            switch (dfd.FormatType)
            {
            case FormatDescriptor.Type.StringGeneric:
            case FormatDescriptor.Type.StringReverse:
                opcodeStr = sDataOpNames.StrGeneric;
                break;

            case FormatDescriptor.Type.StringNullTerm:
                opcodeStr     = sDataOpNames.StrNullTerm;
                trailingBytes = 1;
                break;

            case FormatDescriptor.Type.StringL8:
                opcodeStr          = sDataOpNames.StrLen8;
                hiddenLeadingBytes = 1;
                break;

            case FormatDescriptor.Type.StringL16:
                opcodeStr         = sDataOpNames.StrGeneric;
                shownLeadingBytes = 2;
                break;

            case FormatDescriptor.Type.StringDci:
                opcodeStr = sDataOpNames.StrDci;
                if ((Project.FileData[offset] & 0x80) != 0)
                {
                    // ".shift" directive only works for strings where the low bit starts
                    // clear and ends high.
                    OutputNoJoy(offset, dfd.Length, labelStr, commentStr);
                    return;
                }
                break;

            default:
                Debug.Assert(false);
                return;
            }

            StringOpFormatter stropf = new StringOpFormatter(SourceFormatter,
                                                             Formatter.DOUBLE_QUOTE_DELIM, StringOpFormatter.RawOutputStyle.CommaSep,
                                                             MAX_OPERAND_LEN, charConv);

            if (dfd.FormatType == FormatDescriptor.Type.StringDci)
            {
                // DCI is awkward because the character encoding flips on the last byte.  Rather
                // than clutter up StringOpFormatter for this rare item, we just accept low/high
                // throughout.
                stropf.CharConv = dciConv;
            }

            // Feed bytes in, skipping over hidden bytes (leading L8, trailing null).
            stropf.FeedBytes(data, offset + hiddenLeadingBytes,
                             dfd.Length - hiddenLeadingBytes - trailingBytes, shownLeadingBytes,
                             StringOpFormatter.ReverseMode.Forward);
            Debug.Assert(stropf.Lines.Count > 0);

            // See if we need to do this over.
            bool redo = false;

            switch (dfd.FormatType)
            {
            case FormatDescriptor.Type.StringGeneric:
            case FormatDescriptor.Type.StringReverse:
            case FormatDescriptor.Type.StringL16:
                // All good the first time.
                break;

            case FormatDescriptor.Type.StringNullTerm:
            case FormatDescriptor.Type.StringL8:
            case FormatDescriptor.Type.StringDci:
                if (stropf.Lines.Count != 1)
                {
                    // Must be single-line.
                    opcodeStr       = sDataOpNames.StrGeneric;
                    stropf.CharConv = charConv;     // undo DCI hack
                    redo            = true;
                }
                break;

            default:
                Debug.Assert(false);
                return;
            }

            if (redo)
            {
                //Debug.WriteLine("REDO off=+" + offset.ToString("x6") + ": " + dfd.FormatType);

                // This time, instead of skipping over leading length bytes, we include them
                // explicitly.
                stropf.Reset();
                stropf.FeedBytes(data, offset, dfd.Length, hiddenLeadingBytes,
                                 StringOpFormatter.ReverseMode.Forward);
            }

            opcodeStr = formatter.FormatPseudoOp(opcodeStr);

            foreach (string str in stropf.Lines)
            {
                OutputLine(labelStr, opcodeStr, str, commentStr);
                labelStr = commentStr = string.Empty;       // only show on first
            }
        }
示例#24
0
        // IGenerator
        public void OutputDataOp(int offset)
        {
            Formatter formatter = SourceFormatter;

            byte[]   data = Project.FileData;
            Anattrib attr = Project.GetAnattrib(offset);

            string labelStr = string.Empty;

            if (attr.Symbol != null)
            {
                labelStr = mLocalizer.ConvLabel(attr.Symbol.Label);
            }

            string commentStr = SourceFormatter.FormatEolComment(Project.Comments[offset]);
            string opcodeStr, operandStr;

            FormatDescriptor dfd = attr.DataDescriptor;

            Debug.Assert(dfd != null);
            int length = dfd.Length;

            Debug.Assert(length > 0);

            bool multiLine = false;

            switch (dfd.FormatType)
            {
            case FormatDescriptor.Type.Default:
                if (length != 1)
                {
                    Debug.Assert(false);
                    length = 1;
                }
                opcodeStr = sDataOpNames.DefineData1;
                int operand = RawData.GetWord(data, offset, length, false);
                operandStr = formatter.FormatHexValue(operand, length * 2);
                break;

            case FormatDescriptor.Type.NumericLE:
                opcodeStr = sDataOpNames.GetDefineData(length);
                operand   = RawData.GetWord(data, offset, length, false);
                UpdateCharacterEncoding(dfd);
                operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable,
                                                           mLocalizer.LabelMap, dfd, operand, length,
                                                           PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix);
                break;

            case FormatDescriptor.Type.NumericBE:
                opcodeStr = sDataOpNames.GetDefineBigData(length);
                if ((string.IsNullOrEmpty(opcodeStr)))
                {
                    // Nothing defined, output as comma-separated single-byte values.
                    GenerateShortSequence(offset, length, out opcodeStr, out operandStr);
                }
                else
                {
                    UpdateCharacterEncoding(dfd);
                    operand    = RawData.GetWord(data, offset, length, true);
                    operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable,
                                                               mLocalizer.LabelMap, dfd, operand, length,
                                                               PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix);
                }
                break;

            case FormatDescriptor.Type.Fill:
                opcodeStr  = sDataOpNames.Fill;
                operandStr = length + "," + formatter.FormatHexValue(data[offset], 2);
                break;

            case FormatDescriptor.Type.Dense:
                multiLine = true;
                opcodeStr = operandStr = null;
                OutputDenseHex(offset, length, labelStr, commentStr);
                break;

            case FormatDescriptor.Type.Junk:
                int fillVal = Helper.CheckRangeHoldsSingleValue(data, offset, length);
                if (fillVal >= 0 && GenCommon.CheckJunkAlign(offset, dfd, Project.AddrMap))
                {
                    // .align <expression>[, <fill>]
                    opcodeStr = sDataOpNames.Align;
                    int alignVal = 1 << FormatDescriptor.AlignmentToPower(dfd.FormatSubType);
                    operandStr = alignVal.ToString() +
                                 "," + formatter.FormatHexValue(fillVal, 2);
                }
                else if (fillVal >= 0)
                {
                    // treat same as Fill
                    opcodeStr  = sDataOpNames.Fill;
                    operandStr = length + "," + formatter.FormatHexValue(fillVal, 2);
                }
                else
                {
                    // treat same as Dense
                    multiLine = true;
                    opcodeStr = operandStr = null;
                    OutputDenseHex(offset, length, labelStr, commentStr);
                }
                break;

            case FormatDescriptor.Type.StringGeneric:
            case FormatDescriptor.Type.StringReverse:
            case FormatDescriptor.Type.StringNullTerm:
            case FormatDescriptor.Type.StringL8:
            case FormatDescriptor.Type.StringL16:
            case FormatDescriptor.Type.StringDci:
                multiLine = true;
                opcodeStr = operandStr = null;
                OutputString(offset, labelStr, commentStr);
                break;

            default:
                opcodeStr  = "???";
                operandStr = "***";
                break;
            }

            if (!multiLine)
            {
                opcodeStr = formatter.FormatPseudoOp(opcodeStr);
                OutputLine(labelStr, opcodeStr, operandStr, commentStr);
            }
        }
示例#25
0
 // IGenerator
 public FormatDescriptor ModifyInstructionOperandFormat(int offset, FormatDescriptor dfd,
                                                        int operand)
 {
     return(dfd);
 }
示例#26
0
 // IGenerator
 public void UpdateCharacterEncoding(FormatDescriptor dfd)
 {
 }
示例#27
0
        private void OutputString(int offset, string labelStr, string commentStr)
        {
            Formatter formatter = SourceFormatter;

            byte[]           data = Project.FileData;
            Anattrib         attr = Project.GetAnattrib(offset);
            FormatDescriptor dfd  = attr.DataDescriptor;

            Debug.Assert(dfd != null);
            Debug.Assert(dfd.IsString);
            Debug.Assert(dfd.Length > 0);

            string opcodeStr;

            CharEncoding.Convert charConv;
            switch (dfd.FormatSubType)
            {
            case FormatDescriptor.SubType.Ascii:
                opcodeStr = sDataOpNames.StrGeneric;
                charConv  = CharEncoding.ConvertAscii;
                break;

            case FormatDescriptor.SubType.HighAscii:
                opcodeStr = sDataOpNames.StrGeneric;
                charConv  = CharEncoding.ConvertHighAscii;
                break;

            case FormatDescriptor.SubType.C64Petscii:
                opcodeStr = "!pet";
                charConv  = CharEncoding.ConvertC64Petscii;
                break;

            case FormatDescriptor.SubType.C64Screen:
                opcodeStr = "!scr";
                charConv  = CharEncoding.ConvertC64ScreenCode;
                break;

            default:
                Debug.Assert(false);
                OutputNoJoy(offset, dfd.Length, labelStr, commentStr);
                return;
            }

            int leadingBytes = 0;

            switch (dfd.FormatType)
            {
            case FormatDescriptor.Type.StringGeneric:
            case FormatDescriptor.Type.StringReverse:
            case FormatDescriptor.Type.StringNullTerm:
            case FormatDescriptor.Type.StringDci:
                // Last byte may be output as hex.
                break;

            case FormatDescriptor.Type.StringL8:
                // Length byte will be output as hex.
                leadingBytes = 1;
                break;

            case FormatDescriptor.Type.StringL16:
                // Length byte will be output as hex.
                leadingBytes = 2;
                break;

            default:
                Debug.Assert(false);
                return;
            }

            StringOpFormatter stropf = new StringOpFormatter(SourceFormatter,
                                                             Formatter.DOUBLE_QUOTE_DELIM, StringOpFormatter.RawOutputStyle.CommaSep, charConv);

            stropf.FeedBytes(data, offset, dfd.Length, leadingBytes,
                             StringOpFormatter.ReverseMode.Forward);

            if (dfd.FormatSubType == FormatDescriptor.SubType.HighAscii && stropf.HasEscapedText)
            {
                // Can't !xor the output, because while it works for string data it
                // also flips the high bits on the unprintable bytes we output as raw hex.
                OutputNoJoy(offset, dfd.Length, labelStr, commentStr);
                return;
            }

            if (dfd.FormatSubType == FormatDescriptor.SubType.HighAscii)
            {
                OutputLine(string.Empty, "!xor", "$80 {", string.Empty);
            }
            foreach (string str in stropf.Lines)
            {
                OutputLine(labelStr, opcodeStr, str, commentStr);
                labelStr = commentStr = string.Empty;       // only show on first
            }
            if (dfd.FormatSubType == FormatDescriptor.SubType.HighAscii)
            {
                OutputLine(string.Empty, "}", string.Empty, string.Empty);
            }
        }
        private bool SaveFile(Document document, string file, FormatDescriptor format)
        {
            if (string.IsNullOrEmpty(file))
            {
                file = document.PathAndFileName;
            }

            if (format == null)
            {
                format = PintaCore.System.ImageFormats.GetFormatByFile(file);
            }

            if (format == null || format.IsReadOnly())
            {
                MessageDialog md = new MessageDialog(PintaCore.Chrome.MainWindow, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, Catalog.GetString("Pinta does not support saving images in this file format."), file);
                md.Title = Catalog.GetString("Error");

                md.Run();
                md.Destroy();
                return(false);
            }

            // If the user tries to save over a read only file, give a more informative error message than "Unhandled Exception"
            FileInfo file_info = new FileInfo(file);

            if (file_info.Exists && file_info.IsReadOnly)
            {
                MessageDialog md = new MessageDialog(PintaCore.Chrome.MainWindow, DialogFlags.Modal, MessageType.Error,
                                                     ButtonsType.Ok, Catalog.GetString("Cannot save read only file."));
                md.Title = Catalog.GetString("Error");

                md.Run();
                md.Destroy();
                return(false);
            }

            // Commit any pending changes
            PintaCore.Tools.Commit();

            try {
                format.Exporter.Export(document, file);
            } catch (GLib.GException e) {             // Errors from GDK
                if (e.Message == "Image too large to be saved as ICO")
                {
                    string primary   = Catalog.GetString("Image too large");
                    string secondary = Catalog.GetString("ICO files can not be larger than 255 x 255 pixels.");
                    string message   = string.Format(markup, primary, secondary);

                    MessageDialog md = new MessageDialog(PintaCore.Chrome.MainWindow, DialogFlags.Modal, MessageType.Error,
                                                         ButtonsType.Ok, message);

                    md.Run();
                    md.Destroy();
                    return(false);
                }
                else
                {
                    throw e;                     // Only catch exceptions we know the reason for
                }
            }

            document.Filename = Path.GetFileName(file);
            document.IsDirty  = false;

            return(true);
        }
        // This is actually both for "Save As" and saving a file that never
        // been saved before.  Either way, we need to prompt for a filename.
        private bool SaveFileAs(Document document)
        {
            var fcd = new FileChooserDialog(Mono.Unix.Catalog.GetString("Save Image File"),
                                            PintaCore.Chrome.MainWindow,
                                            FileChooserAction.Save,
                                            Gtk.Stock.Cancel,
                                            Gtk.ResponseType.Cancel,
                                            Gtk.Stock.Save, Gtk.ResponseType.Ok);

            fcd.DoOverwriteConfirmation = true;
            fcd.SetCurrentFolder(PintaCore.System.GetDialogDirectory());
            fcd.AlternativeButtonOrder = new int[] { (int)ResponseType.Ok, (int)ResponseType.Cancel };

            bool hasFile = document.HasFile;

            if (hasFile)
            {
                fcd.SetFilename(document.PathAndFileName);
            }

            Dictionary <FileFilter, FormatDescriptor> filetypes = new Dictionary <FileFilter, FormatDescriptor> ();

            // Add all the formats we support to the save dialog
            foreach (var format in PintaCore.System.ImageFormats.Formats)
            {
                if (!format.IsReadOnly())
                {
                    fcd.AddFilter(format.Filter);
                    filetypes.Add(format.Filter, format);

                    // Set the filter to anything we found
                    // We want to ensure that *something* is selected in the filetype
                    fcd.Filter = format.Filter;
                }
            }

            // If we already have a format, set it to the default.
            // If not, default to jpeg
            FormatDescriptor format_desc = null;

            if (hasFile)
            {
                format_desc = PintaCore.System.ImageFormats.GetFormatByFile(document.Filename);
            }

            if (format_desc == null)
            {
                format_desc = PintaCore.System.ImageFormats.GetDefaultSaveFormat();

                // Gtk doesn't like it if we set the file name to an extension that we don't have
                // a filter for, so we change the extension to our default extension.
                if (hasFile)
                {
                    fcd.SetFilename(Path.ChangeExtension(document.PathAndFileName, format_desc.Extensions[0]));
                }
            }

            fcd.Filter = format_desc.Filter;

            fcd.AddNotification("filter", this.OnFilterChanged);

            // Replace GTK's ConfirmOverwrite with our own, for UI consistency
            fcd.ConfirmOverwrite += (eventSender, eventArgs) => {
                if (this.ConfirmOverwrite(fcd, fcd.Filename))
                {
                    eventArgs.RetVal = FileChooserConfirmation.AcceptFilename;
                }
                else
                {
                    eventArgs.RetVal = FileChooserConfirmation.SelectAgain;
                }
            };

            while (fcd.Run() == (int)Gtk.ResponseType.Ok)
            {
                FormatDescriptor format = filetypes[fcd.Filter];
                string           file   = fcd.Filename;

                if (string.IsNullOrEmpty(Path.GetExtension(file)))
                {
                    // No extension; add one from the format descriptor.
                    file            = string.Format("{0}.{1}", file, format.Extensions[0]);
                    fcd.CurrentName = Path.GetFileName(file);

                    // We also need to display an overwrite confirmation message manually,
                    // because MessageDialog won't do this for us in this case.
                    if (File.Exists(file) && !ConfirmOverwrite(fcd, file))
                    {
                        continue;
                    }
                }

                // Always follow the extension rather than the file type drop down
                // ie: if the user chooses to save a "jpeg" as "foo.png", we are going
                // to assume they just didn't update the dropdown and really want png
                var format_type = PintaCore.System.ImageFormats.GetFormatByFile(file);

                if (format_type != null)
                {
                    format = format_type;
                }

                PintaCore.System.LastDialogDirectory = fcd.CurrentFolder;

                // If saving the file failed or was cancelled, let the user select
                // a different file type.
                if (!SaveFile(document, file, format, fcd))
                {
                    continue;
                }

                //The user is saving the Document to a new file, so technically it
                //hasn't been saved to its associated file in this session.
                document.HasBeenSavedInSession = false;

                RecentManager.Default.AddFull(fcd.Uri, PintaCore.System.RecentData);
                PintaCore.System.ImageFormats.SetDefaultFormat(Path.GetExtension(file));

                document.HasFile         = true;
                document.PathAndFileName = file;

                fcd.Destroy();
                return(true);
            }

            fcd.Destroy();
            return(false);
        }
示例#30
0
        private static void GenerateInstruction(IGenerator gen, StreamWriter sw,
                                                LocalVariableLookup lvLookup, int offset, int instrBytes, bool doAddCycles)
        {
            DisasmProject proj      = gen.Project;
            Formatter     formatter = gen.SourceFormatter;

            byte[]   data = proj.FileData;
            Anattrib attr = proj.GetAnattrib(offset);

            string labelStr = string.Empty;

            if (attr.Symbol != null)
            {
                labelStr = gen.Localizer.ConvLabel(attr.Symbol.Label);
            }

            OpDef op       = proj.CpuDef.GetOpDef(data[offset]);
            int   operand  = op.GetOperand(data, offset, attr.StatusFlags);
            int   instrLen = op.GetLength(attr.StatusFlags);

            OpDef.WidthDisambiguation wdis = OpDef.WidthDisambiguation.None;
            if (op.IsWidthPotentiallyAmbiguous)
            {
                wdis = OpDef.GetWidthDisambiguation(instrLen, operand);
            }
            if (gen.Quirks.SinglePassAssembler && wdis == OpDef.WidthDisambiguation.None &&
                (op.AddrMode == OpDef.AddressMode.DP ||
                 op.AddrMode == OpDef.AddressMode.DPIndexX) ||
                op.AddrMode == OpDef.AddressMode.DPIndexY)
            {
                // Could be a forward reference to a direct-page label.  For ACME, we don't
                // care if it's forward or not.
                if ((gen.Quirks.SinglePassNoLabelCorrection && IsLabelReference(gen, offset)) ||
                    IsForwardLabelReference(gen, offset))
                {
                    wdis = OpDef.WidthDisambiguation.ForceDirect;
                }
            }

            string opcodeStr = formatter.FormatOpcode(op, wdis);

            string formattedOperand = null;
            int    operandLen       = instrLen - 1;

            PseudoOp.FormatNumericOpFlags opFlags = PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix;
            bool isPcRelBankWrap = false;

            // Tweak branch instructions.  We want to show the absolute address rather
            // than the relative offset (which happens with the OperandAddress assignment
            // below), and 1-byte branches should always appear as a 4-byte hex value.
            if (op.AddrMode == OpDef.AddressMode.PCRel)
            {
                Debug.Assert(attr.OperandAddress >= 0);
                operandLen = 2;
                opFlags   |= PseudoOp.FormatNumericOpFlags.IsPcRel;
            }
            else if (op.AddrMode == OpDef.AddressMode.PCRelLong ||
                     op.AddrMode == OpDef.AddressMode.StackPCRelLong)
            {
                opFlags |= PseudoOp.FormatNumericOpFlags.IsPcRel;
            }
            else if (op.AddrMode == OpDef.AddressMode.Imm ||
                     op.AddrMode == OpDef.AddressMode.ImmLongA ||
                     op.AddrMode == OpDef.AddressMode.ImmLongXY)
            {
                opFlags |= PseudoOp.FormatNumericOpFlags.HasHashPrefix;
            }
            if ((opFlags & PseudoOp.FormatNumericOpFlags.IsPcRel) != 0)
            {
                int branchDist = attr.Address - attr.OperandAddress;
                isPcRelBankWrap = branchDist > 32767 || branchDist < -32768;
            }

            // 16-bit operands outside bank 0 need to include the bank when computing
            // symbol adjustment.
            int operandForSymbol = operand;

            if (attr.OperandAddress >= 0)
            {
                operandForSymbol = attr.OperandAddress;
            }

            // Check Length to watch for bogus descriptors.  (ApplyFormatDescriptors() should
            // now be screening bad descriptors out, so we may not need the Length test.)
            if (attr.DataDescriptor != null && attr.Length == attr.DataDescriptor.Length)
            {
                FormatDescriptor dfd = gen.ModifyInstructionOperandFormat(offset,
                                                                          attr.DataDescriptor, operand);

                // Format operand as directed.
                if (op.AddrMode == OpDef.AddressMode.BlockMove)
                {
                    // Special handling for the double-operand block move.
                    string opstr1 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
                                                                  gen.Localizer.LabelMap, dfd, operand >> 8, 1,
                                                                  PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix);
                    string opstr2 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
                                                                  gen.Localizer.LabelMap, dfd, operand & 0xff, 1,
                                                                  PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix);
                    if (gen.Quirks.BlockMoveArgsReversed)
                    {
                        string tmp = opstr1;
                        opstr1 = opstr2;
                        opstr2 = tmp;
                    }
                    string hash = gen.Quirks.BlockMoveArgsNoHash ? "" : "#";
                    formattedOperand = hash + opstr1 + "," + hash + opstr2;
                }
                else
                {
                    if (attr.DataDescriptor.IsStringOrCharacter)
                    {
                        gen.UpdateCharacterEncoding(dfd);
                    }
                    formattedOperand = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
                                                                     lvLookup, gen.Localizer.LabelMap, dfd,
                                                                     offset, operandForSymbol, operandLen, opFlags);
                }
            }
            else
            {
                // Show operand value in hex.
                if (op.AddrMode == OpDef.AddressMode.BlockMove)
                {
                    int arg1, arg2;
                    if (gen.Quirks.BlockMoveArgsReversed)
                    {
                        arg1 = operand & 0xff;
                        arg2 = operand >> 8;
                    }
                    else
                    {
                        arg1 = operand >> 8;
                        arg2 = operand & 0xff;
                    }
                    string hash = gen.Quirks.BlockMoveArgsNoHash ? "" : "#";
                    formattedOperand = hash + formatter.FormatHexValue(arg1, 2) + "," +
                                       hash + formatter.FormatHexValue(arg2, 2);
                }
                else
                {
                    if (operandLen == 2)
                    {
                        // This is necessary for 16-bit operands, like "LDA abs" and "PEA val",
                        // when outside bank zero.  The bank is included in the operand address,
                        // but we don't want to show it here.
                        operandForSymbol &= 0xffff;
                    }
                    formattedOperand = formatter.FormatHexValue(operandForSymbol, operandLen * 2);
                }
            }
            string operandStr = formatter.FormatOperand(op, formattedOperand, wdis);

            if (gen.Quirks.StackIntOperandIsImmediate && op.AddrMode == OpDef.AddressMode.StackInt)
            {
                // COP $02 is standard, but some require COP #$02
                operandStr = '#' + operandStr;
            }

            string eolComment = proj.Comments[offset];

            if (doAddCycles)
            {
                bool branchCross = (attr.Address & 0xff00) != (operandForSymbol & 0xff00);
                int  cycles      = proj.CpuDef.GetCycles(op.Opcode, attr.StatusFlags, attr.BranchTaken,
                                                         branchCross);
                if (cycles > 0)
                {
                    if (!string.IsNullOrEmpty(eolComment))
                    {
                        eolComment = cycles.ToString() + "  " + eolComment;
                    }
                    else
                    {
                        eolComment = cycles.ToString();
                    }
                }
                else
                {
                    if (!string.IsNullOrEmpty(eolComment))
                    {
                        eolComment = (-cycles).ToString() + "+ " + eolComment;
                    }
                    else
                    {
                        eolComment = (-cycles).ToString() + "+";
                    }
                }
            }
            string commentStr = formatter.FormatEolComment(eolComment);

            string replMnemonic = gen.ModifyOpcode(offset, op);

            if (attr.Length != instrBytes)
            {
                // This instruction has another instruction inside it.  Throw out what we
                // computed and just output as bytes.
                gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr);
            }
            else if (isPcRelBankWrap && gen.Quirks.NoPcRelBankWrap)
            {
                // Some assemblers have trouble generating PC-relative operands that wrap
                // around the bank.  Output as raw hex.
                gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr);
            }
            else if (op.AddrMode == OpDef.AddressMode.BlockMove &&
                     gen.Quirks.BlockMoveArgsReversed)
            {
                // On second thought, just don't even output the wrong thing.
                gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr);
            }
            else if (replMnemonic == null)
            {
                // No mnemonic exists for this opcode.
                gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr);
            }
            else if (replMnemonic != string.Empty)
            {
                // A replacement mnemonic has been provided.
                opcodeStr = formatter.FormatMnemonic(replMnemonic, wdis);
            }
            gen.OutputLine(labelStr, opcodeStr, operandStr, commentStr);

            // Assemblers like Merlin32 try to be helpful and track SEP/REP, but they do the
            // wrong thing if we're in emulation mode.  Force flags back to short.
            if (proj.CpuDef.HasEmuFlag && gen.Quirks.TracksSepRepNotEmu && op == OpDef.OpREP_Imm)
            {
                if ((operand & 0x30) != 0 && attr.StatusFlags.E == 1)
                {
                    gen.OutputRegWidthDirective(offset, 0, 0, 1, 1);
                }
            }
        }