示例#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
        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
            }
        }
示例#3
0
        private void OutputString(int offset, string labelStr, string commentStr)
        {
            // Normal ASCII strings are straightforward: they're just part of a .byte
            // directive, and can mix with anything else in the .byte.
            //
            // For CString we can use .asciiz, but only if the string fits on one line
            // and doesn't include delimiters.  For L8String and L16String we can
            // define simple macros, but their use has a similar restriction.  High-ASCII
            // strings also require a macro.
            //
            // We might be able to define a macro for DCI and Reverse as well.
            //
            // The limitation on strings with delimiters arises because (1) I don't see a
            // way to escape them within a string, and (2) the simple macro workarounds
            // only take a single argument, not a comma-separated list of stuff.
            //
            // Some ideas here:
            // https://groups.google.com/forum/#!topic/comp.sys.apple2.programmer/5Wkw8mUPcU0

            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;
            bool isHighAscii = false;

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

            case FormatDescriptor.SubType.HighAscii:
                if (dfd.FormatType != FormatDescriptor.Type.StringGeneric)
                {
                    OutputNoJoy(offset, dfd.Length, labelStr, commentStr);
                    return;
                }
                charConv    = CharEncoding.ConvertHighAscii;
                isHighAscii = true;
                break;

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

            Formatter formatter = SourceFormatter;

            byte[] data          = Project.FileData;
            int    leadingBytes  = 0;
            int    trailingBytes = 0;

            switch (dfd.FormatType)
            {
            case FormatDescriptor.Type.StringGeneric:
            case FormatDescriptor.Type.StringReverse:
            case FormatDescriptor.Type.StringDci:
                break;

            case FormatDescriptor.Type.StringNullTerm:
                trailingBytes = 1;
                break;

            case FormatDescriptor.Type.StringL8:
                leadingBytes = 1;
                break;

            case FormatDescriptor.Type.StringL16:
                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 - trailingBytes, leadingBytes,
                             StringOpFormatter.ReverseMode.Forward);

            string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.StrGeneric);

            if (isHighAscii)
            {
                // Does this fit the narrow definition of what we can do with a macro?
                Debug.Assert(dfd.FormatType == FormatDescriptor.Type.StringGeneric);
                if (stropf.Lines.Count == 1 && !stropf.HasEscapedText)
                {
                    if (!mHighAsciiMacroOutput)
                    {
                        mHighAsciiMacroOutput = true;
                        // Output a macro for high-ASCII strings.
                        //
                        // TODO(maybe): the preferred way to do this is apparently
                        // ".macpack apple2" to load some standard macros, then e.g.
                        // scrcode "My high-ASCII string".  The macro takes 9 arguments and
                        // recognizes characters and numbers, so it should be possible to
                        // mix strings, string delimiters, and control chars so long as the
                        // argument count is not exceeded.
                        OutputLine(".macro", "HiAscii", "Arg", string.Empty);
                        OutputLine(string.Empty, ".repeat", ".strlen(Arg), I", string.Empty);
                        OutputLine(string.Empty, ".byte", ".strat(Arg, I) | $80", string.Empty);
                        OutputLine(string.Empty, ".endrep", string.Empty, string.Empty);
                        OutputLine(".endmacro", string.Empty, string.Empty, string.Empty);
                    }
                    opcodeStr = formatter.FormatPseudoOp("HiAscii");
                }
                else
                {
                    // didn't work out, dump hex
                    OutputNoJoy(offset, dfd.Length, labelStr, commentStr);
                    return;
                }
            }

            if (dfd.FormatType == FormatDescriptor.Type.StringNullTerm)
            {
                if (stropf.Lines.Count == 1 && !stropf.HasEscapedText)
                {
                    // Keep it.
                    opcodeStr = sDataOpNames.StrNullTerm;
                }
                else
                {
                    // Didn't fit, so re-emit it, this time with the terminating null byte.
                    stropf.Reset();
                    stropf.FeedBytes(data, offset, dfd.Length, leadingBytes,
                                     StringOpFormatter.ReverseMode.Forward);
                }
            }

            foreach (string str in stropf.Lines)
            {
                OutputLine(labelStr, opcodeStr, str, commentStr);
                labelStr = commentStr = string.Empty;       // only show on first
            }
        }