Пример #1
0
        /// <summary>
        /// Computes the number of lines of output required to hold the formatted output.
        /// </summary>
        /// <param name="formatter">Format definition.</param>
        /// <param name="dfd">Data format descriptor.</param>
        /// <returns>Line count.</returns>
        public static int ComputeRequiredLineCount(Formatter formatter, FormatDescriptor dfd)
        {
            switch (dfd.FormatType)
            {
            case FormatDescriptor.Type.Default:
            case FormatDescriptor.Type.NumericLE:
            case FormatDescriptor.Type.NumericBE:
            case FormatDescriptor.Type.Fill:
                return(1);

            case FormatDescriptor.Type.Dense: {
                // no delimiter, two output bytes per input byte
                int maxLen  = MAX_OPERAND_LEN;
                int textLen = dfd.Length * 2;
                return((textLen + maxLen - 1) / maxLen);
            }

            case FormatDescriptor.Type.String: {
                // Subtract two chars, to leave room for start/end delimiter.  We use
                // non-ASCII delimiters on-screen, so there's nothing to escape there.
                int maxLen = MAX_OPERAND_LEN - 2;

                // Remove leading length or trailing null byte from string length.
                int textLen = dfd.Length;
                switch (dfd.FormatSubType)
                {
                case FormatDescriptor.SubType.None:
                case FormatDescriptor.SubType.Dci:
                case FormatDescriptor.SubType.Reverse:
                case FormatDescriptor.SubType.DciReverse:
                    break;

                case FormatDescriptor.SubType.CString:
                case FormatDescriptor.SubType.L8String:
                    textLen--;
                    break;

                case FormatDescriptor.SubType.L16String:
                    textLen -= 2;
                    break;

                default:
                    Debug.Assert(false);
                    break;
                }
                int strLen = (textLen + maxLen - 1) / maxLen;
                if (strLen == 0)
                {
                    // Empty string, but we still need to output a line.
                    strLen = 1;
                }
                return(strLen);
            }

            default:
                Debug.Assert(false);
                return(1);
            }
        }
Пример #2
0
        /// <summary>
        /// Constructs a DefSymbol from a Symbol and a format descriptor.  This is used
        /// for project symbols.
        /// </summary>
        /// <param name="sym">Base symbol.</param>
        /// <param name="dfd">Format descriptor.</param>
        /// <param name="comment">End-of-line comment.</param>
        public DefSymbol(Symbol sym, FormatDescriptor dfd, string comment)
            : this(sym.Label, sym.Value, sym.SymbolSource, sym.SymbolType)
        {
            Debug.Assert(comment != null);

            DataDescriptor = dfd;
            Comment        = comment;
            Tag            = string.Empty;
        }
Пример #3
0
 public SerFormatDescriptor(FormatDescriptor dfd)
 {
     Length    = dfd.Length;
     Format    = dfd.FormatType.ToString();
     SubFormat = dfd.FormatSubType.ToString();
     if (dfd.SymbolRef != null)
     {
         SymbolRef = new SerWeakSymbolRef(dfd.SymbolRef);
     }
 }
Пример #4
0
        /// <summary>
        /// Creates an UndoableChange for an operand or data format update.
        /// </summary>
        /// <param name="offset">Affected offset.</param>
        /// <param name="oldFormat">Current format.  May be null.</param>
        /// <param name="newFormat">New format.  May be null.</param>
        /// <returns>Change record.</returns>
        public static UndoableChange CreateOperandFormatChange(int offset,
                                                               FormatDescriptor oldFormat, FormatDescriptor newFormat)
        {
            if (oldFormat == newFormat)
            {
                Debug.WriteLine("No-op format change at +" + offset.ToString("x6") +
                                ": " + oldFormat);
            }

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

            UndoableChange uc = new UndoableChange();

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

            // Data-only reanalysis is required if the old or new format has a label.  Simply
            // changing from e.g. default to decimal, or decimal to binary, doesn't matter.
            // (The format editing code ensures that labels don't appear in the middle of
            // a formatted region.)  Adding, removing, or changing a symbol can change the
            // layout of uncategorized data, affect data targets, xrefs, etc.
            //
            // We can't only check for a symbol, though, because Numeric/Address will
            // create an auto-label if the reference is within the file.
            //
            // If the number of bytes covered by the format changes, or we're adding or
            // removing a format, we need to redo the analysis of uncategorized data.  For
            // example, an auto-detected string could get larger or smaller.  We don't
            // currently have a separate flag for just that.  Also, because we're focused
            // on just one change, we can't skip reanalysis when (say) one 4-byte numeric
            // is converted to two two-byte numerics.
            if ((oldFormat != null && oldFormat.HasSymbolOrAddress) ||
                (newFormat != null && newFormat.HasSymbolOrAddress))
            {
                uc.ReanalysisRequired = ReanalysisScope.DataOnly;
            }
            else if (oldFormat == null || newFormat == null ||
                     oldFormat.Length != newFormat.Length)
            {
                uc.ReanalysisRequired = ReanalysisScope.DataOnly;
            }
            else
            {
                uc.ReanalysisRequired = ReanalysisScope.None;
            }
            return(uc);
        }
Пример #5
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="label">Symbol's label.</param>
        /// <param name="value">Symbol's value.</param>
        /// <param name="source">Symbol source (general point of origin).</param>
        /// <param name="type">Symbol type.</param>
        /// <param name="formatSubType">Format descriptor sub-type, so we know how the
        ///   user wants the value to be displayed.</param>
        /// <param name="comment">End-of-line comment.</param>
        /// <param name="tag">Symbol tag, used for grouping platform symbols.</param>
        public DefSymbol(string label, int value, Source source, Type type,
                         FormatDescriptor.SubType formatSubType, string comment, string tag)
            : this(label, value, source, type)
        {
            Debug.Assert(comment != null);
            Debug.Assert(tag != null);

            // Length doesn't matter; use 1 to get prefab object.
            DataDescriptor = FormatDescriptor.Create(1,
                                                     FormatDescriptor.Type.NumericLE, formatSubType);

            Comment = comment;
            Tag     = tag;
        }
Пример #6
0
        /// <summary>
        /// Creates an UndoableChange for an operand or data format update.  This method
        /// refuses to create a change for a no-op, returning null instead.  This will
        /// convert a FormatDescriptor with type REMOVE to null, with the intention of
        /// removing the descriptor from the format set.
        /// </summary>
        /// <param name="offset">Affected offset.</param>
        /// <param name="oldFormat">Current format.  May be null.</param>
        /// <param name="newFormat">New format.  May be null.</param>
        /// <returns>Change record, or null for a no-op change.</returns>
        public static UndoableChange CreateActualOperandFormatChange(int offset,
                                                                     FormatDescriptor oldFormat, FormatDescriptor newFormat)
        {
            if (newFormat != null && newFormat.FormatType == FormatDescriptor.Type.REMOVE)
            {
                Debug.WriteLine("CreateOperandFormatChange: converting REMOVE to null");
                newFormat = null;
            }
            if (oldFormat == newFormat)
            {
                Debug.WriteLine("No-op format change at +" + offset.ToString("x6") +
                                ": " + oldFormat);
                return(null);
            }

            return(CreateOperandFormatChange(offset, oldFormat, newFormat));
        }
Пример #7
0
 /// <summary>
 /// Debugging utility function to dump a sorted list of objects.
 /// </summary>
 public static void DebugDumpSortedList(SortedList <int, FormatDescriptor> list)
 {
     if (list == null)
     {
         Debug.WriteLine("FormatDescriptor list is empty");
         return;
     }
     Debug.WriteLine("FormatDescriptor list (" + list.Count + " entries)");
     foreach (KeyValuePair <int, FormatDescriptor> kvp in list)
     {
         int offset           = kvp.Key;
         FormatDescriptor dfd = kvp.Value;
         Debug.WriteLine(" +" + offset.ToString("x6") + ",+" +
                         (offset + dfd.Length - 1).ToString("x6") + ": " + dfd.FormatType +
                         "(" + dfd.FormatSubType + ")");
     }
 }
Пример #8
0
 /// <summary>
 /// Creates a FormatDescriptor from a SerFormatDescriptor.
 /// </summary>
 /// <param name="sfd">Deserialized data.</param>
 /// <param name="report">Error report object.</param>
 /// <param name="dfd">Created FormatDescriptor.</param>
 /// <returns>True on success.</returns>
 private static bool CreateFormatDescriptor(SerFormatDescriptor sfd,
                                            FileLoadReport report, out FormatDescriptor dfd)
 {
     dfd = null;
     FormatDescriptor.Type    format;
     FormatDescriptor.SubType subFormat;
     try {
         format = (FormatDescriptor.Type)Enum.Parse(
             typeof(FormatDescriptor.Type), sfd.Format);
         subFormat = (FormatDescriptor.SubType)Enum.Parse(
             typeof(FormatDescriptor.SubType), sfd.SubFormat);
     } catch (ArgumentException) {
         report.Add(FileLoadItem.Type.Warning, Res.Strings.ERR_BAD_FD_FORMAT +
                    ": " + sfd.Format + "/" + sfd.SubFormat);
         return(false);
     }
     if (sfd.SymbolRef == null)
     {
         dfd = FormatDescriptor.Create(sfd.Length, format, subFormat);
     }
     else
     {
         WeakSymbolRef.Part part;
         try {
             part = (WeakSymbolRef.Part)Enum.Parse(
                 typeof(WeakSymbolRef.Part), sfd.SymbolRef.Part);
         } catch (ArgumentException) {
             report.Add(FileLoadItem.Type.Warning,
                        Res.Strings.ERR_BAD_SYMREF_PART +
                        ": " + sfd.SymbolRef.Part);
             return(false);
         }
         dfd = FormatDescriptor.Create(sfd.Length,
                                       new WeakSymbolRef(sfd.SymbolRef.Label, part),
                                       format == FormatDescriptor.Type.NumericBE);
     }
     return(true);
 }
Пример #9
0
        /// <summary>
        /// Format the symbol and adjustment using cc65 expression syntax.
        /// </summary>
        private static void FormatNumericSymbolCc65(Formatter formatter, Symbol sym,
                                                    Dictionary <string, string> labelMap, FormatDescriptor dfd,
                                                    int operandValue, int operandLen, FormatNumericOpFlags flags, StringBuilder sb)
        {
            // The key difference between cc65 and other assemblers with general expressions
            // is that the bitwise shift and AND operators have higher precedence than the
            // arithmetic ops like add and subtract.  (The bitwise ops are equal to multiply
            // and divide.)  This means that, if we want to mask off the low 16 bits and add one
            // to a label, we can write "start & $ffff + 1" rather than "(start & $ffff) + 1".
            //
            // This is particularly convenient for PEA, since "PEA (start & $ffff)" looks like
            // we're trying to use a (non-existent) indirect form of PEA.  We can write things
            // in a simpler way.

            int adjustment, symbolValue;

            string symLabel = sym.Label;

            if (labelMap != null && labelMap.TryGetValue(symLabel, out string newLabel))
            {
                symLabel = newLabel;
            }

            if (operandLen == 1)
            {
                // Use the byte-selection operator to get the right piece.
                string selOp;
                if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank)
                {
                    symbolValue = (sym.Value >> 16) & 0xff;
                    selOp       = "^";
                }
                else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High)
                {
                    symbolValue = (sym.Value >> 8) & 0xff;
                    selOp       = ">";
                }
                else
                {
                    symbolValue = sym.Value & 0xff;
                    if (symbolValue == sym.Value)
                    {
                        selOp = string.Empty;
                    }
                    else
                    {
                        selOp = "<";
                    }
                }
                sb.Append(selOp);
                sb.Append(symLabel);

                operandValue &= 0xff;
            }
            else if (operandLen <= 4)
            {
                uint   mask = 0xffffffff >> ((4 - operandLen) * 8);
                string shOp;
                if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank)
                {
                    symbolValue = (sym.Value >> 16);
                    shOp        = " >> 16";
                }
                else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High)
                {
                    symbolValue = (sym.Value >> 8);
                    shOp        = " >> 8";
                }
                else
                {
                    symbolValue = sym.Value;
                    shOp        = "";
                }

                if (flags == FormatNumericOpFlags.IsPcRel)
                {
                    // PC-relative operands are funny, because an 8- or 16-bit value is always
                    // expanded to 24 bits.  We output a 16-bit value that the assembler will
                    // convert back to 8-bit or 16-bit.  In any event, the bank byte is never
                    // relevant to our computations.
                    operandValue &= 0xffff;
                    symbolValue  &= 0xffff;
                }

                sb.Append(symLabel);
                sb.Append(shOp);
                if (symbolValue > mask)
                {
                    // Post-shift value won't fit in an operand-size box.
                    symbolValue = (int)(symbolValue & mask);
                    sb.Append(" & ");
                    sb.Append(formatter.FormatHexValue((int)mask, 2));
                }
                operandValue = (int)(operandValue & mask);

                if (sb.Length != symLabel.Length)
                {
                    sb.Append(' ');
                }
            }
            else
            {
                Debug.Assert(false, "bad numeric len");
                sb.Append("?????");
                symbolValue = 0;
            }

            adjustment = operandValue - symbolValue;

            sb.Append(formatter.FormatAdjustment(adjustment));
        }
Пример #10
0
        /// <summary>
        /// Format the symbol and adjustment using common expression syntax.
        /// </summary>
        private static void FormatNumericSymbolCommon(Formatter formatter, Symbol sym,
                                                      Dictionary <string, string> labelMap, FormatDescriptor dfd,
                                                      int operandValue, int operandLen, FormatNumericOpFlags flags, StringBuilder sb)
        {
            // We could have some simple code that generated correct output, shifting and
            // masking every time, but that's ugly and annoying.  For single-byte ops we can
            // just use the byte-select operators, for wider ops we get only as fancy as we
            // need to be.

            int adjustment, symbolValue;

            string symLabel = sym.Label;

            if (labelMap != null && labelMap.TryGetValue(symLabel, out string newLabel))
            {
                symLabel = newLabel;
            }

            if (operandLen == 1)
            {
                // Use the byte-selection operator to get the right piece.  In 64tass the
                // selection operator has a very low precedence, similar to Merlin 32.
                string selOp;
                if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank)
                {
                    symbolValue = (sym.Value >> 16) & 0xff;
                    if (formatter.Config.mBankSelectBackQuote)
                    {
                        selOp = "`";
                    }
                    else
                    {
                        selOp = "^";
                    }
                }
                else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High)
                {
                    symbolValue = (sym.Value >> 8) & 0xff;
                    selOp       = ">";
                }
                else
                {
                    symbolValue = sym.Value & 0xff;
                    if (symbolValue == sym.Value)
                    {
                        selOp = string.Empty;
                    }
                    else
                    {
                        selOp = "<";
                    }
                }

                operandValue &= 0xff;

                if (operandValue != symbolValue &&
                    dfd.SymbolRef.ValuePart != WeakSymbolRef.Part.Low)
                {
                    // Adjustment is required to an upper-byte part.
                    sb.Append('(');
                    sb.Append(selOp);
                    sb.Append(symLabel);
                    sb.Append(')');
                }
                else
                {
                    // no adjustment required
                    sb.Append(selOp);
                    sb.Append(symLabel);
                }
            }
            else if (operandLen <= 4)
            {
                // Operands and values should be 8/16/24 bit unsigned quantities.  32-bit
                // support is really there so you can have a 24-bit pointer in a 32-bit hole.
                // Might need to adjust this if 32-bit signed quantities become interesting.
                uint mask = 0xffffffff >> ((4 - operandLen) * 8);
                int  rightShift;
                if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank)
                {
                    symbolValue = (sym.Value >> 16);
                    rightShift  = 16;
                }
                else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High)
                {
                    symbolValue = (sym.Value >> 8);
                    rightShift  = 8;
                }
                else
                {
                    symbolValue = sym.Value;
                    rightShift  = 0;
                }

                if (flags == FormatNumericOpFlags.IsPcRel)
                {
                    // PC-relative operands are funny, because an 8- or 16-bit value is always
                    // expanded to 24 bits.  We output a 16-bit value that the assembler will
                    // convert back to 8-bit or 16-bit.  In any event, the bank byte is never
                    // relevant to our computations.
                    operandValue &= 0xffff;
                    symbolValue  &= 0xffff;
                }

                bool needMask = false;
                if (symbolValue > mask)
                {
                    // Post-shift value won't fit in an operand-size box.
                    symbolValue = (int)(symbolValue & mask);
                    needMask    = true;
                }

                operandValue = (int)(operandValue & mask);

                // Generate one of:
                //  label [+ adj]
                //  (label >> rightShift) [+ adj]
                //  (label & mask) [+ adj]
                //  ((label >> rightShift) & mask) [+ adj]

                if (rightShift != 0 || needMask)
                {
                    if (flags != FormatNumericOpFlags.HasHashPrefix)
                    {
                        sb.Append("0+");
                    }
                    if (rightShift != 0 && needMask)
                    {
                        sb.Append("((");
                    }
                    else
                    {
                        sb.Append("(");
                    }
                }
                sb.Append(symLabel);

                if (rightShift != 0)
                {
                    sb.Append(" >> ");
                    sb.Append(rightShift.ToString());
                    sb.Append(')');
                }

                if (needMask)
                {
                    sb.Append(" & ");
                    sb.Append(formatter.FormatHexValue((int)mask, 2));
                    sb.Append(')');
                }
            }
            else
            {
                Debug.Assert(false, "bad numeric len");
                sb.Append("?????");
                symbolValue = 0;
            }

            adjustment = operandValue - symbolValue;

            sb.Append(formatter.FormatAdjustment(adjustment));
        }
Пример #11
0
        /// <summary>
        /// Format a numeric operand value according to the specified sub-format.
        /// </summary>
        /// <param name="formatter">Text formatter.</param>
        /// <param name="symbolTable">Full table of project symbols.</param>
        /// <param name="labelMap">Symbol label remap, for local label conversion.  May be
        ///   null.</param>
        /// <param name="dfd">Operand format descriptor.</param>
        /// <param name="operandValue">Operand's value.  For most things this comes directly
        ///   out of the code, for relative branches it's a 24-bit absolute address.</param>
        /// <param name="operandLen">Length of operand, in bytes.  For an instruction, this
        ///   does not include the opcode byte.  For a relative branch, this will be 2.</param>
        /// <param name="flags">Special handling.</param>
        public static string FormatNumericOperand(Formatter formatter, SymbolTable symbolTable,
                                                  Dictionary <string, string> labelMap, FormatDescriptor dfd,
                                                  int operandValue, int operandLen, FormatNumericOpFlags flags)
        {
            Debug.Assert(operandLen > 0);
            int hexMinLen = operandLen * 2;

            switch (dfd.FormatSubType)
            {
            case FormatDescriptor.SubType.None:
            case FormatDescriptor.SubType.Hex:
            case FormatDescriptor.SubType.Address:
                return(formatter.FormatHexValue(operandValue, hexMinLen));

            case FormatDescriptor.SubType.Decimal:
                return(formatter.FormatDecimalValue(operandValue));

            case FormatDescriptor.SubType.Binary:
                return(formatter.FormatBinaryValue(operandValue, hexMinLen * 4));

            case FormatDescriptor.SubType.Ascii:
                return(formatter.FormatAsciiOrHex(operandValue));

            case FormatDescriptor.SubType.Symbol:
                if (symbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym))
                {
                    StringBuilder sb = new StringBuilder();

                    switch (formatter.ExpressionMode)
                    {
                    case Formatter.FormatConfig.ExpressionMode.Common:
                        FormatNumericSymbolCommon(formatter, sym, labelMap,
                                                  dfd, operandValue, operandLen, flags, sb);
                        break;

                    case Formatter.FormatConfig.ExpressionMode.Cc65:
                        FormatNumericSymbolCc65(formatter, sym, labelMap,
                                                dfd, operandValue, operandLen, flags, sb);
                        break;

                    case Formatter.FormatConfig.ExpressionMode.Merlin:
                        FormatNumericSymbolMerlin(formatter, sym, labelMap,
                                                  dfd, operandValue, operandLen, flags, sb);
                        break;

                    default:
                        Debug.Assert(false, "Unknown expression mode " +
                                     formatter.ExpressionMode);
                        return("???");
                    }

                    return(sb.ToString());
                }
                else
                {
                    return(formatter.FormatHexValue(operandValue, hexMinLen));
                }

            default:
                Debug.Assert(false);
                return("???");
            }
        }
Пример #12
0
        /// <summary>
        /// Generates a pseudo-op statement for the specified data operation.
        ///
        /// For most operations, only one output line will be generated.  For larger items,
        /// like long comments, the value may be split into multiple lines.  The sub-index
        /// indicates which line should be formatted.
        /// </summary>
        /// <param name="formatter">Format definition.</param>
        /// <param name="opNames">Table of pseudo-op names.</param>
        /// <param name="symbolTable">Project symbol table.</param>
        /// <param name="labelMap">Symbol label map.  May be null.</param>
        /// <param name="dfd">Data format descriptor.</param>
        /// <param name="data">File data array.</param>
        /// <param name="offset">Start offset.</param>
        /// <param name="subIndex">For multi-line items, which line.</param>
        public static PseudoOut FormatDataOp(Formatter formatter, PseudoOpNames opNames,
                                             SymbolTable symbolTable, Dictionary <string, string> labelMap,
                                             FormatDescriptor dfd, byte[] data, int offset, int subIndex)
        {
            if (dfd == null)
            {
                // should never happen
                //Debug.Assert(false, "Null dfd at offset+" + offset.ToString("x6"));
                PseudoOut failed = new PseudoOut();
                failed.Opcode = failed.Operand = "!FAILED!+" + offset.ToString("x6");
                return(failed);
            }

            int length = dfd.Length;

            Debug.Assert(length > 0);

            // All outputs for a given offset show the same offset and length, even for
            // multi-line items.
            PseudoOut po = new PseudoOut();

            switch (dfd.FormatType)
            {
            case FormatDescriptor.Type.Default:
                if (length != 1)
                {
                    // This shouldn't happen.
                    Debug.Assert(false);
                    length = 1;
                }
                po.Opcode = opNames.GetDefineData(length);
                int operand = RawData.GetWord(data, offset, length, false);
                po.Operand = formatter.FormatHexValue(operand, length * 2);
                break;

            case FormatDescriptor.Type.NumericLE:
                po.Opcode  = opNames.GetDefineData(length);
                operand    = RawData.GetWord(data, offset, length, false);
                po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd,
                                                  operand, length, FormatNumericOpFlags.None);
                break;

            case FormatDescriptor.Type.NumericBE:
                po.Opcode  = opNames.GetDefineBigData(length);
                operand    = RawData.GetWord(data, offset, length, true);
                po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd,
                                                  operand, length, FormatNumericOpFlags.None);
                break;

            case FormatDescriptor.Type.Fill:
                po.Opcode  = opNames.Fill;
                po.Operand = length + "," + formatter.FormatHexValue(data[offset], 2);
                break;

            case FormatDescriptor.Type.Dense: {
                int maxPerLine = MAX_OPERAND_LEN / 2;
                offset += subIndex * maxPerLine;
                length -= subIndex * maxPerLine;
                if (length > maxPerLine)
                {
                    length = maxPerLine;
                }
                po.Opcode  = opNames.Dense;
                po.Operand = formatter.FormatDenseHex(data, offset, length);
                //List<PseudoOut> outList = new List<PseudoOut>();
                //GenerateTextLines(text, "", "", po, outList);
                //po = outList[subIndex];
            }
            break;

            case FormatDescriptor.Type.String:
                // It's hard to do strings in single-line pieces because of prefix lengths,
                // terminating nulls, DCI polarity, and reverse-order strings.  We
                // really just want to convert the whole thing to a run of chars
                // and then pull out a chunk.  As an optimization we can handle
                // generic strings (subtype=None) more efficiently, which should solve
                // the problem of massive strings created by auto-analysis.
                if (dfd.FormatSubType == FormatDescriptor.SubType.None)
                {
                    int maxPerLine = MAX_OPERAND_LEN - 2;
                    offset += subIndex * maxPerLine;
                    length -= subIndex * maxPerLine;
                    if (length > maxPerLine)
                    {
                        length = maxPerLine;
                    }
                    char[] ltext = BytesToChars(formatter, opNames, dfd.FormatSubType, data,
                                                offset, length, out string lpopcode, out int unused);
                    po.Opcode  = lpopcode;
                    po.Operand = "\u201c" + new string(ltext) + "\u201d";
                }
                else
                {
                    char[] text = BytesToChars(formatter, opNames, dfd.FormatSubType, data,
                                               offset, length, out string popcode, out int showHexZeroes);

                    if (showHexZeroes == 1)
                    {
                        po.Opcode  = opNames.DefineData1;
                        po.Operand = formatter.FormatHexValue(0, 2);
                    }
                    else if (showHexZeroes == 2)
                    {
                        po.Opcode  = opNames.DefineData2;
                        po.Operand = formatter.FormatHexValue(0, 4);
                    }
                    else
                    {
                        Debug.Assert(showHexZeroes == 0);
                        po.Opcode = popcode;
                        List <PseudoOut> outList = new List <PseudoOut>();
                        GenerateTextLines(text, "\u201c", "\u201d", po, outList);
                        po = outList[subIndex];
                    }
                }
                break;

            default:
                Debug.Assert(false);
                po.Opcode  = ".???";
                po.Operand = "$" + data[offset].ToString("x2");
                break;
            }

            return(po);
        }
Пример #13
0
        /// <summary>
        /// Format the symbol and adjustment using Merlin expression syntax.
        /// </summary>
        private static void FormatNumericSymbolMerlin(Formatter formatter, Symbol sym,
                                                      Dictionary <string, string> labelMap, FormatDescriptor dfd,
                                                      int operandValue, int operandLen, FormatNumericOpFlags flags, StringBuilder sb)
        {
            // Merlin expressions are compatible with the original 8-bit Merlin.  They're
            // evaluated from left to right, with (almost) no regard for operator precedence.
            //
            // The part-selection operators differ from "simple" in two ways:
            //  (1) They always happen last.  If FOO=$10f0, "#>FOO+$18" == $11.  One of the
            //      few cases where left-to-right evaluation is overridden.
            //  (2) They select words, not bytes.  If FOO=$123456, "#>FOO" is $1234.  This is
            //      best thought of as a shift operator, rather than byte-selection.  For
            //      8-bit code this doesn't matter.
            //
            // This behavior leads to simpler expressions for simple symbol adjustments.

            string symLabel = sym.Label;

            if (labelMap != null && labelMap.TryGetValue(symLabel, out string newLabel))
            {
                symLabel = newLabel;
            }

            int adjustment;

            // If we add or subtract an adjustment, it will be done on the full value, which
            // is then shifted to the appropriate part.  So we need to left-shift the operand
            // value to match.  We fill in the low bytes with the contents of the symbol, so
            // that the adjustment doesn't include unnecessary values.  (For example, let
            // FOO=$10f0, with operand "#>FOO" ($10).  We shift the operand to get $1000, then
            // OR in the low byte to get $10f0, so that when we subtract we get adjustment==0.)
            int adjOperand, keepLen;

            if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank)
            {
                adjOperand = operandValue << 16 | (int)(sym.Value & 0xff00ffff);
                keepLen    = 3;
            }
            else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High)
            {
                adjOperand = (operandValue << 8) | (sym.Value & 0xff);
                keepLen    = 2;
            }
            else
            {
                adjOperand = operandValue;
                keepLen    = 1;
            }

            keepLen    = Math.Max(keepLen, operandLen);
            adjustment = adjOperand - sym.Value;
            if (keepLen == 1)
            {
                adjustment %= 256;
                // Adjust for aesthetics.  The assembler implicitly applies a modulo operation,
                // so we can use the value closest to zero.
                if (adjustment > 127)
                {
                    adjustment = -(256 - adjustment) /*% 256*/;
                }
                else if (adjustment < -128)
                {
                    adjustment = (256 + adjustment) /*% 256*/;
                }
            }
            else if (keepLen == 2)
            {
                adjustment %= 65536;
                if (adjustment > 32767)
                {
                    adjustment = -(65536 - adjustment) /*% 65536*/;
                }
                else if (adjustment < -32768)
                {
                    adjustment = (65536 + adjustment) /*% 65536*/;
                }
            }

            // Use the label from sym, not dfd's weak ref; might be different if label
            // comparisons are case-insensitive.
            switch (dfd.SymbolRef.ValuePart)
            {
            case WeakSymbolRef.Part.Unknown:
            case WeakSymbolRef.Part.Low:
                // For Merlin, "<" is effectively a no-op.  We can put it in for
                // aesthetics when grabbing the low byte of a 16-bit value.
                if ((operandLen == 1) && sym.Value > 0xff)
                {
                    sb.Append('<');
                }
                sb.Append(symLabel);
                break;

            case WeakSymbolRef.Part.High:
                sb.Append('>');
                sb.Append(symLabel);
                break;

            case WeakSymbolRef.Part.Bank:
                sb.Append('^');
                sb.Append(symLabel);
                break;

            default:
                Debug.Assert(false, "bad part");
                sb.Append("???");
                break;
            }

            sb.Append(formatter.FormatAdjustment(adjustment));
        }
Пример #14
0
        /// <summary>
        /// Loads platform symbols.
        /// </summary>
        /// <param name="fileIdent">Relative pathname of file to open.</param>
        /// <param name="projectDir">Full path to project directory.</param>
        /// <param name="report">Report of warnings and errors.</param>
        /// <returns>True on success (no errors), false on failure.</returns>
        public bool LoadFromFile(string fileIdent, string projectDir, out FileLoadReport report)
        {
            // These files shouldn't be enormous.  Do it the easy way.
            report = new FileLoadReport(fileIdent);

            ExternalFile ef = ExternalFile.CreateFromIdent(fileIdent);

            if (ef == null)
            {
                report.Add(FileLoadItem.Type.Error,
                           CommonUtil.Properties.Resources.ERR_FILE_NOT_FOUND + ": " + fileIdent);
                return(false);
            }

            string pathName = ef.GetPathName(projectDir);

            if (pathName == null)
            {
                report.Add(FileLoadItem.Type.Error,
                           Res.Strings.ERR_BAD_IDENT + ": " + fileIdent);
                return(false);
            }
            string[] lines;
            try {
                lines = File.ReadAllLines(pathName);
            } catch (IOException ioe) {
                Debug.WriteLine("Platform symbol load failed: " + ioe);
                report.Add(FileLoadItem.Type.Error,
                           CommonUtil.Properties.Resources.ERR_FILE_NOT_FOUND + ": " + pathName);
                return(false);
            }

            string tag = string.Empty;

            int lineNum = 0;

            foreach (string line in lines)
            {
                lineNum++;      // first line is line 1, says Vim and VisualStudio
                if (string.IsNullOrEmpty(line) || line[0] == ';')
                {
                    // ignore
                }
                else if (line[0] == '*')
                {
                    if (line.StartsWith(TAG_CMD))
                    {
                        tag = ParseTag(line);
                    }
                    else
                    {
                        // Do something clever with *SYNOPSIS?
                        Debug.WriteLine("CMD: " + line);
                    }
                }
                else
                {
                    MatchCollection matches = sNameValueRegex.Matches(line);
                    if (matches.Count == 1)
                    {
                        //Debug.WriteLine("GOT '" + matches[0].Groups[1] + "' " +
                        //    matches[0].Groups[2] + " '" + matches[0].Groups[3] + "'");
                        string label   = matches[0].Groups[1].Value;
                        bool   isConst = (matches[0].Groups[2].Value[0] == '=');
                        string badParseMsg;
                        int    value, numBase;
                        bool   parseOk;
                        if (isConst)
                        {
                            // Allow various numeric options, and preserve the value.
                            parseOk = Asm65.Number.TryParseInt(matches[0].Groups[3].Value,
                                                               out value, out numBase);
                            badParseMsg =
                                CommonUtil.Properties.Resources.ERR_INVALID_NUMERIC_CONSTANT;
                        }
                        else
                        {
                            // Allow things like "05/1000".  Always hex.
                            numBase = 16;
                            parseOk = Asm65.Address.ParseAddress(matches[0].Groups[3].Value,
                                                                 (1 << 24) - 1, out value);
                            badParseMsg = CommonUtil.Properties.Resources.ERR_INVALID_ADDRESS;
                        }
                        if (!parseOk)
                        {
                            report.Add(lineNum, FileLoadItem.NO_COLUMN, FileLoadItem.Type.Warning,
                                       badParseMsg);
                        }
                        else
                        {
                            string comment = matches[0].Groups[4].Value;
                            if (comment.Length > 0)
                            {
                                // remove ';'
                                comment = comment.Substring(1);
                            }
                            FormatDescriptor.SubType subType =
                                FormatDescriptor.GetSubTypeForBase(numBase);
                            DefSymbol symDef = new DefSymbol(label, value, Symbol.Source.Platform,
                                                             isConst ? Symbol.Type.Constant : Symbol.Type.ExternalAddr,
                                                             subType, comment, tag);
                            if (mSymbols.ContainsKey(label))
                            {
                                // This is very easy to do -- just define the same symbol twice
                                // in the same file.  We don't really need to do anything about
                                // it though.
                                Debug.WriteLine("NOTE: stomping previous definition of " + label);
                            }
                            mSymbols[label] = symDef;
                        }
                    }
                    else
                    {
                        report.Add(lineNum, FileLoadItem.NO_COLUMN, FileLoadItem.Type.Warning,
                                   CommonUtil.Properties.Resources.ERR_SYNTAX);
                    }
                }
            }

            return(!report.HasErrors);
        }