Пример #1
0
        private void OutputString(int offset, string labelStr, string commentStr)
        {
            // This gets complicated.
            //
            // For Dci, L8String, and L16String, the entire string needs to fit in the
            // operand of one line.  If it can't, we need to separate the length byte/word
            // or inverted character out, and just dump the rest as ASCII.  Computing the
            // line length requires factoring delimiter character escapes.  (NOTE: contrary
            // to the documentation, STR and STRL do include trailing hex characters in the
            // length calculation, so it's possible to escape delimiters.)
            //
            // For Reverse, we can span lines, but only if we emit the lines in
            // backward order.  Also, Merlin doesn't allow hex to be embedded in a REV
            // operation, so we can't use REV if the string contains a delimiter.
            //
            // For aesthetic purposes, zero-length CString, L8String, and L16String
            // should be output as DFB/DW zeroes rather than an empty string -- makes
            // it easier to read.
            //
            // NOTE: we generally assume that the input is in the correct format, e.g.
            // the length byte in a StringL8 matches dfd.Length, and the high bits in DCI strings
            // have the right pattern.  If not, we will generate bad output.  This would need
            // to be scanned and corrected at a higher level.

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

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

            // We can sort of do parts of C64 stuff, but it's probably more readable to just
            // output a commented blob than something where only the capital letters are readable.
            switch (dfd.FormatSubType)
            {
            case FormatDescriptor.SubType.Ascii:
            case FormatDescriptor.SubType.HighAscii:
                break;

            case FormatDescriptor.SubType.C64Petscii:
            case FormatDescriptor.SubType.C64Screen:
            default:
                OutputNoJoy(offset, dfd.Length, labelStr, commentStr);
                return;
            }

            Formatter formatter = SourceFormatter;

            byte[] data = Project.FileData;
            StringOpFormatter.ReverseMode revMode = StringOpFormatter.ReverseMode.Forward;
            int    leadingBytes = 0;
            string opcodeStr;

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

            case FormatDescriptor.Type.StringReverse:
                opcodeStr = sDataOpNames.StrReverse;
                revMode   = StringOpFormatter.ReverseMode.LineReverse;
                break;

            case FormatDescriptor.Type.StringNullTerm:
                opcodeStr = sDataOpNames.StrGeneric;            // no pseudo-op for this
                if (dfd.Length == 1)
                {
                    // Empty string.  Just output the length byte(s) or null terminator.
                    GenerateShortSequence(offset, 1, out string opcode, out string operand);
                    OutputLine(labelStr, opcode, operand, commentStr);
                    return;
                }
                break;

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

            case FormatDescriptor.Type.StringL16:
                opcodeStr    = sDataOpNames.StrLen16;
                leadingBytes = 2;
                break;

            case FormatDescriptor.Type.StringDci:
                opcodeStr = sDataOpNames.StrDci;
                break;

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

            // Merlin 32 uses single-quote for low ASCII, double-quote for high ASCII.
            CharEncoding.Convert charConv;
            char delim;

            if (dfd.FormatSubType == FormatDescriptor.SubType.HighAscii)
            {
                charConv = CharEncoding.ConvertHighAscii;
                delim    = '"';
            }
            else
            {
                charConv = CharEncoding.ConvertAscii;
                delim    = '\'';
            }

            StringOpFormatter stropf = new StringOpFormatter(SourceFormatter,
                                                             new Formatter.DelimiterDef(delim),
                                                             StringOpFormatter.RawOutputStyle.DenseHex, 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 = CharEncoding.ConvertLowAndHighAscii;
            }

            // Feed bytes in, skipping over the leading length bytes.
            stropf.FeedBytes(data, offset + leadingBytes,
                             dfd.Length - leadingBytes, 0, revMode);
            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.StringNullTerm:
                break;

            case FormatDescriptor.Type.StringReverse:
                if (stropf.HasEscapedText)
                {
                    // can't include escaped characters in REV
                    opcodeStr = sDataOpNames.StrGeneric;
                    revMode   = StringOpFormatter.ReverseMode.Forward;
                    redo      = true;
                }
                break;

            case FormatDescriptor.Type.StringL8:
                if (stropf.Lines.Count != 1)
                {
                    // single-line only
                    opcodeStr    = sDataOpNames.StrGeneric;
                    leadingBytes = 1;
                    redo         = true;
                }
                break;

            case FormatDescriptor.Type.StringL16:
                if (stropf.Lines.Count != 1)
                {
                    // single-line only
                    opcodeStr    = sDataOpNames.StrGeneric;
                    leadingBytes = 2;
                    redo         = true;
                }
                break;

            case FormatDescriptor.Type.StringDci:
                if (stropf.Lines.Count != 1)
                {
                    // single-line only
                    opcodeStr       = sDataOpNames.StrGeneric;
                    stropf.CharConv = charConv;
                    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, leadingBytes, revMode);
            }

            opcodeStr = formatter.FormatPseudoOp(opcodeStr);

            foreach (string str in stropf.Lines)
            {
                OutputLine(labelStr, opcodeStr, str, commentStr);
                labelStr = commentStr = string.Empty;       // only show on first
            }
        }
Пример #2
0
        /// <summary>
        /// Converts a collection of bytes that represent a string into an array of formatted
        /// string operands.
        /// </summary>
        /// <param name="formatter">Formatter object.</param>
        /// <param name="opNames">Pseudo-opcode name table.</param>
        /// <param name="dfd">Format descriptor.</param>
        /// <param name="data">File data.</param>
        /// <param name="offset">Offset, within data, of start of string.</param>
        /// <param name="popcode">Pseudo-opcode string.</param>
        /// <returns>Array of operand strings.</returns>
        public static List <string> FormatStringOp(Formatter formatter, PseudoOpNames opNames,
                                                   FormatDescriptor dfd, byte[] data, int offset, out string popcode)
        {
            int hiddenLeadingBytes = 0;
            int trailingBytes      = 0;

            StringOpFormatter.ReverseMode revMode = StringOpFormatter.ReverseMode.Forward;
            Formatter.DelimiterSet        delSet  = formatter.Config.mStringDelimiters;
            Formatter.DelimiterDef        delDef;

            CharEncoding.Convert charConv;
            switch (dfd.FormatSubType)
            {
            case FormatDescriptor.SubType.Ascii:
                if (dfd.FormatType == FormatDescriptor.Type.StringDci)
                {
                    charConv = CharEncoding.ConvertLowAndHighAscii;
                }
                else
                {
                    charConv = CharEncoding.ConvertAscii;
                }
                delDef = delSet.Get(CharEncoding.Encoding.Ascii);
                break;

            case FormatDescriptor.SubType.HighAscii:
                if (dfd.FormatType == FormatDescriptor.Type.StringDci)
                {
                    charConv = CharEncoding.ConvertLowAndHighAscii;
                }
                else
                {
                    charConv = CharEncoding.ConvertHighAscii;
                }
                delDef = delSet.Get(CharEncoding.Encoding.HighAscii);
                break;

            case FormatDescriptor.SubType.C64Petscii:
                if (dfd.FormatType == FormatDescriptor.Type.StringDci)
                {
                    charConv = CharEncoding.ConvertLowAndHighC64Petscii;
                }
                else
                {
                    charConv = CharEncoding.ConvertC64Petscii;
                }
                delDef = delSet.Get(CharEncoding.Encoding.C64Petscii);
                break;

            case FormatDescriptor.SubType.C64Screen:
                if (dfd.FormatType == FormatDescriptor.Type.StringDci)
                {
                    charConv = CharEncoding.ConvertLowAndHighC64ScreenCode;
                }
                else
                {
                    charConv = CharEncoding.ConvertC64ScreenCode;
                }
                delDef = delSet.Get(CharEncoding.Encoding.C64ScreenCode);
                break;

            default:
                Debug.Assert(false);
                charConv = CharEncoding.ConvertAscii;
                delDef   = delSet.Get(CharEncoding.Encoding.Ascii);
                break;
            }

            if (delDef == null)
            {
                delDef = Formatter.DOUBLE_QUOTE_DELIM;
            }

            switch (dfd.FormatType)
            {
            case FormatDescriptor.Type.StringGeneric:
                // Generic character data.
                popcode = opNames.StrGeneric;
                break;

            case FormatDescriptor.Type.StringReverse:
                // Character data, full width specified by formatter.  Show characters
                // in reverse order.
                popcode = opNames.StrReverse;
                revMode = StringOpFormatter.ReverseMode.FullReverse;
                break;

            case FormatDescriptor.Type.StringNullTerm:
                // Character data with a terminating null.  Don't show the null byte.
                popcode       = opNames.StrNullTerm;
                trailingBytes = 1;
                //if (strLen == 0) {
                //    showHexZeroes = 1;
                //}
                break;

            case FormatDescriptor.Type.StringL8:
                // Character data with a leading length byte.  Don't show the length.
                hiddenLeadingBytes = 1;
                //if (strLen == 0) {
                //    showHexZeroes = 1;
                //}
                popcode = opNames.StrLen8;
                break;

            case FormatDescriptor.Type.StringL16:
                // Character data with a leading length word.  Don't show the length.
                Debug.Assert(dfd.Length > 1);
                hiddenLeadingBytes = 2;
                //if (strLen == 0) {
                //    showHexZeroes = 2;
                //}
                popcode = opNames.StrLen16;
                break;

            case FormatDescriptor.Type.StringDci:
                // High bit on last byte is flipped.
                popcode = opNames.StrDci;
                break;

            default:
                Debug.Assert(false);
                popcode = ".!!!";
                break;
            }

            StringOpFormatter stropf = new StringOpFormatter(formatter, delDef,
                                                             StringOpFormatter.RawOutputStyle.CommaSep, MAX_OPERAND_LEN, charConv);

            stropf.FeedBytes(data, offset + hiddenLeadingBytes,
                             dfd.Length - hiddenLeadingBytes - trailingBytes, 0, revMode);

            return(stropf.Lines);
        }