/// <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; } }
/// <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); }
/// <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); }
// 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); }
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); }
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; }
// 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); }
/// <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)); }
// 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); }
// 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; } }
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; }
/// <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); } }
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; } } }
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); }
/// <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); }
// 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; }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
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 } }
// 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); } }
// IGenerator public FormatDescriptor ModifyInstructionOperandFormat(int offset, FormatDescriptor dfd, int operand) { return(dfd); }
// IGenerator public void UpdateCharacterEncoding(FormatDescriptor dfd) { }
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); }
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); } } }