Beispiel #1
0
        /// <summary>
        /// Feeds the bytes into the StringGather.
        /// </summary>
        private void FeedGath(StringGather gath, byte[] data, int offset, int length,
                              int leadingBytes, bool showLeading, int trailingBytes, bool showTrailing)
        {
            int startOffset  = offset;
            int strEndOffset = offset + length - trailingBytes;

            if (showLeading)
            {
                while (leadingBytes-- > 0)
                {
                    gath.WriteByte(data[offset++]);
                }
            }
            else
            {
                offset += leadingBytes;
            }
            for (; offset < strEndOffset; offset++)
            {
                gath.WriteChar((char)(data[offset] & 0x7f));
            }
            while (showTrailing && trailingBytes-- > 0)
            {
                gath.WriteByte(data[offset++]);
            }
            gath.Finish();
        }
Beispiel #2
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

            Formatter formatter = SourceFormatter;

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

            Debug.Assert(dfd != null);
            Debug.Assert(dfd.FormatType == FormatDescriptor.Type.String);
            Debug.Assert(dfd.Length > 0);

            bool highAscii     = false;
            int  leadingBytes  = 0;
            int  trailingBytes = 0;
            bool showLeading   = false;
            bool showTrailing  = false;

            switch (dfd.FormatSubType)
            {
            case FormatDescriptor.SubType.None:
                highAscii = (data[offset] & 0x80) != 0;
                break;

            case FormatDescriptor.SubType.Dci:
                highAscii     = (data[offset] & 0x80) != 0;
                trailingBytes = 1;
                showTrailing  = true;
                break;

            case FormatDescriptor.SubType.Reverse:
                highAscii = (data[offset] & 0x80) != 0;
                break;

            case FormatDescriptor.SubType.DciReverse:
                highAscii    = (data[offset + dfd.Length - 1] & 0x80) != 0;
                leadingBytes = 1;
                showLeading  = true;
                break;

            case FormatDescriptor.SubType.CString:
                highAscii     = (data[offset] & 0x80) != 0;
                trailingBytes = 1;
                showTrailing  = true;
                break;

            case FormatDescriptor.SubType.L8String:
                if (dfd.Length > 1)
                {
                    highAscii = (data[offset + 1] & 0x80) != 0;
                }
                leadingBytes = 1;
                showLeading  = true;
                break;

            case FormatDescriptor.SubType.L16String:
                if (dfd.Length > 2)
                {
                    highAscii = (data[offset + 2] & 0x80) != 0;
                }
                leadingBytes = 2;
                showLeading  = true;
                break;

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

            char         delim = '"';
            StringGather gath  = null;

            // Run the string through so we can see if it'll fit on one line.  As a minor
            // optimization, we skip this step for "generic" strings, which are probably
            // the most common thing.
            if (dfd.FormatSubType != FormatDescriptor.SubType.None || highAscii)
            {
                gath = new StringGather(this, labelStr, "???", commentStr, delim,
                                        delim, StringGather.ByteStyle.CommaSep, MAX_OPERAND_LEN, true);
                FeedGath(gath, data, offset, dfd.Length, leadingBytes, showLeading,
                         trailingBytes, showTrailing);
                Debug.Assert(gath.NumLinesOutput > 0);
            }

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

            switch (dfd.FormatSubType)
            {
            case FormatDescriptor.SubType.None:
                // Special case for simple short high-ASCII strings.  These have no
                // leading or trailing bytes.  We can improve this a bit by handling
                // arbitrarily long strings by simply breaking them across lines.
                Debug.Assert(leadingBytes == 0);
                Debug.Assert(trailingBytes == 0);
                if (highAscii && gath.NumLinesOutput == 1 && !gath.HasDelimiter)
                {
                    if (!mHighAsciiMacroOutput)
                    {
                        mHighAsciiMacroOutput = true;
                        // Output a macro for high-ASCII strings.
                        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");
                    highAscii = false;
                }
                break;

            case FormatDescriptor.SubType.Dci:
            case FormatDescriptor.SubType.Reverse:
            case FormatDescriptor.SubType.DciReverse:
                // Full configured above.
                break;

            case FormatDescriptor.SubType.CString:
                if (gath.NumLinesOutput == 1 && !gath.HasDelimiter)
                {
                    opcodeStr    = sDataOpNames.StrNullTerm;
                    showTrailing = false;
                }
                break;

            case FormatDescriptor.SubType.L8String:
            case FormatDescriptor.SubType.L16String:
                // Implement macros?
                break;

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

            if (highAscii)
            {
                OutputNoJoy(offset, dfd.Length, labelStr, commentStr);
                return;
            }

            // Create a new StringGather, with the final opcode choice.
            gath = new StringGather(this, labelStr, opcodeStr, commentStr, delim,
                                    delim, StringGather.ByteStyle.CommaSep, MAX_OPERAND_LEN, false);
            FeedGath(gath, data, offset, dfd.Length, leadingBytes, showLeading,
                     trailingBytes, showTrailing);
        }
Beispiel #3
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.
            //
            // DciReverse is deprecated, but we can handle it as a Reverse string with a
            // trailing byte on a following line.
            //
            // 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.

            Formatter formatter = SourceFormatter;

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

            Debug.Assert(dfd != null);
            Debug.Assert(dfd.FormatType == FormatDescriptor.Type.String);
            Debug.Assert(dfd.Length > 0);

            bool    highAscii     = false;
            int     showZeroes    = 0;
            int     leadingBytes  = 0;
            int     trailingBytes = 0;
            bool    showLeading   = false;
            bool    showTrailing  = false;
            RevMode revMode       = RevMode.Forward;

            switch (dfd.FormatSubType)
            {
            case FormatDescriptor.SubType.None:
                highAscii = (data[offset] & 0x80) != 0;
                break;

            case FormatDescriptor.SubType.Dci:
                highAscii = (data[offset] & 0x80) != 0;
                break;

            case FormatDescriptor.SubType.Reverse:
                highAscii = (data[offset] & 0x80) != 0;
                revMode   = RevMode.Reverse;
                break;

            case FormatDescriptor.SubType.DciReverse:
                highAscii = (data[offset + dfd.Length - 1] & 0x80) != 0;
                revMode   = RevMode.Reverse;
                break;

            case FormatDescriptor.SubType.CString:
                highAscii = (data[offset] & 0x80) != 0;
                if (dfd.Length == 1)
                {
                    showZeroes = 1;         // empty null-terminated string
                }
                trailingBytes = 1;
                showTrailing  = true;
                break;

            case FormatDescriptor.SubType.L8String:
                if (dfd.Length > 1)
                {
                    highAscii = (data[offset + 1] & 0x80) != 0;
                }
                else
                {
                    //showZeroes = 1;
                }
                leadingBytes = 1;
                break;

            case FormatDescriptor.SubType.L16String:
                if (dfd.Length > 2)
                {
                    highAscii = (data[offset + 2] & 0x80) != 0;
                }
                else
                {
                    //showZeroes = 2;
                }
                leadingBytes = 2;
                break;

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

            if (showZeroes != 0)
            {
                // Empty string.  Just output the length byte(s) or null terminator.
                GenerateShortSequence(offset, showZeroes, out string opcode, out string operand);
                OutputLine(labelStr, opcode, operand, commentStr);
                return;
            }

            // Merlin 32 uses single-quote for low ASCII, double-quote for high ASCII.  When
            // quoting the delimiter we use a hexadecimal value.  We need to bear in mind that
            // we're forcing the characters to low ASCII, but the actual character being
            // escaped might be in high ASCII.  Hence delim vs. delimReplace.
            char         delim        = highAscii ? '"' : '\'';
            char         delimReplace = highAscii ? ((char)(delim | 0x80)) : delim;
            StringGather gath         = null;

            // Run the string through so we can see if it'll fit on one line.  As a minor
            // optimization, we skip this step for "generic" strings, which are probably
            // the most common thing.
            if (dfd.FormatSubType != FormatDescriptor.SubType.None)
            {
                gath = new StringGather(this, labelStr, "???", commentStr, delim,
                                        delimReplace, StringGather.ByteStyle.DenseHex, MAX_OPERAND_LEN, true);
                FeedGath(gath, data, offset, dfd.Length, revMode, leadingBytes, showLeading,
                         trailingBytes, showTrailing);
                Debug.Assert(gath.NumLinesOutput > 0);
            }

            string opcodeStr;

            switch (dfd.FormatSubType)
            {
            case FormatDescriptor.SubType.None:
                opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric;
                break;

            case FormatDescriptor.SubType.Dci:
                if (gath.NumLinesOutput == 1)
                {
                    opcodeStr = highAscii ? sDataOpNames.StrDciHi : sDataOpNames.StrDci;
                }
                else
                {
                    opcodeStr     = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric;
                    trailingBytes = 1;
                    showTrailing  = true;
                }
                break;

            case FormatDescriptor.SubType.Reverse:
                if (gath.HasDelimiter)
                {
                    // can't include escaped delimiters in REV
                    opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric;
                    revMode   = RevMode.Forward;
                }
                else if (gath.NumLinesOutput > 1)
                {
                    opcodeStr = highAscii ? sDataOpNames.StrReverseHi : sDataOpNames.StrReverse;
                    revMode   = RevMode.BlockReverse;
                }
                else
                {
                    opcodeStr = highAscii ? sDataOpNames.StrReverseHi : sDataOpNames.StrReverse;
                    Debug.Assert(revMode == RevMode.Reverse);
                }
                break;

            case FormatDescriptor.SubType.DciReverse:
                // Mostly punt -- output as ASCII with special handling for first byte.
                opcodeStr    = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric;
                revMode      = RevMode.Forward;
                leadingBytes = 1;
                showLeading  = true;
                break;

            case FormatDescriptor.SubType.CString:
                //opcodeStr = sDataOpNames.StrNullTerm[highAscii ? 1 : 0];
                opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric;
                break;

            case FormatDescriptor.SubType.L8String:
                if (gath.NumLinesOutput == 1)
                {
                    opcodeStr = highAscii ? sDataOpNames.StrLen8Hi : sDataOpNames.StrLen8;
                }
                else
                {
                    opcodeStr    = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric;
                    leadingBytes = 1;
                    showLeading  = true;
                }
                break;

            case FormatDescriptor.SubType.L16String:
                if (gath.NumLinesOutput == 1)
                {
                    opcodeStr = highAscii ? sDataOpNames.StrLen16Hi : sDataOpNames.StrLen16;
                }
                else
                {
                    opcodeStr    = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric;
                    leadingBytes = 2;
                    showLeading  = true;
                }
                break;

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

            opcodeStr = formatter.FormatPseudoOp(opcodeStr);

            // Create a new StringGather, with the final opcode choice.
            gath = new StringGather(this, labelStr, opcodeStr, commentStr, delim,
                                    delimReplace, StringGather.ByteStyle.DenseHex, MAX_OPERAND_LEN, false);
            FeedGath(gath, data, offset, dfd.Length, revMode, leadingBytes, showLeading,
                     trailingBytes, showTrailing);
        }
Beispiel #4
0
        /// <summary>
        /// Feeds the bytes into the StringGather.
        /// </summary>
        private void FeedGath(StringGather gath, byte[] data, int offset, int length,
                              RevMode revMode, int leadingBytes, bool showLeading,
                              int trailingBytes, bool showTrailing)
        {
            int startOffset  = offset;
            int strEndOffset = offset + length - trailingBytes;

            if (showLeading)
            {
                while (leadingBytes-- > 0)
                {
                    gath.WriteByte(data[offset++]);
                }
            }
            else
            {
                offset += leadingBytes;
            }
            if (revMode == RevMode.BlockReverse)
            {
                const int maxPerLine    = MAX_OPERAND_LEN - 2;
                int       numBlockLines = (length + maxPerLine - 1) / maxPerLine;

                for (int chunk = 0; chunk < numBlockLines; chunk++)
                {
                    int chunkOffset = startOffset + chunk * maxPerLine;
                    int endOffset   = chunkOffset + maxPerLine;
                    if (endOffset > strEndOffset)
                    {
                        endOffset = strEndOffset;
                    }
                    for (int off = endOffset - 1; off >= chunkOffset; off--)
                    {
                        gath.WriteChar((char)(data[off] & 0x7f));
                    }
                }
            }
            else
            {
                for (; offset < strEndOffset; offset++)
                {
                    if (revMode == RevMode.Forward)
                    {
                        gath.WriteChar((char)(data[offset] & 0x7f));
                    }
                    else if (revMode == RevMode.Reverse)
                    {
                        int posn = startOffset + (strEndOffset - offset) - 1;
                        gath.WriteChar((char)(data[posn] & 0x7f));
                    }
                    else
                    {
                        Debug.Assert(false);
                    }
                }
            }
            while (showTrailing && trailingBytes-- > 0)
            {
                gath.WriteByte(data[offset++]);
            }
            gath.Finish();
        }
Beispiel #5
0
        private void OutputString(int offset, string labelStr, string commentStr)
        {
            // Normal ASCII strings are handled 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 could probably do something fancy with the character encoding options to
            // make high-ASCII work nicely.
            //
            // We might be able to define a macro for DCI and Reverse.

            Formatter formatter = SourceFormatter;

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

            Debug.Assert(dfd != null);
            Debug.Assert(dfd.FormatType == FormatDescriptor.Type.String);
            Debug.Assert(dfd.Length > 0);

            bool highAscii     = false;
            int  leadingBytes  = 0;
            int  trailingBytes = 0;
            bool showLeading   = false;
            bool showTrailing  = false;

            switch (dfd.FormatSubType)
            {
            case FormatDescriptor.SubType.None:
                highAscii = (data[offset] & 0x80) != 0;
                break;

            case FormatDescriptor.SubType.Dci:
                highAscii     = (data[offset] & 0x80) != 0;
                trailingBytes = 1;
                showTrailing  = true;
                break;

            case FormatDescriptor.SubType.Reverse:
                highAscii = (data[offset] & 0x80) != 0;
                break;

            case FormatDescriptor.SubType.DciReverse:
                highAscii    = (data[offset + dfd.Length - 1] & 0x80) != 0;
                leadingBytes = 1;
                showLeading  = true;
                break;

            case FormatDescriptor.SubType.CString:
                highAscii     = (data[offset] & 0x80) != 0;
                trailingBytes = 1;
                showTrailing  = true;
                break;

            case FormatDescriptor.SubType.L8String:
                if (dfd.Length > 1)
                {
                    highAscii = (data[offset + 1] & 0x80) != 0;
                }
                leadingBytes = 1;
                showLeading  = true;
                break;

            case FormatDescriptor.SubType.L16String:
                if (dfd.Length > 2)
                {
                    highAscii = (data[offset + 2] & 0x80) != 0;
                }
                leadingBytes = 2;
                showLeading  = true;
                break;

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

            char         delim = '"';
            StringGather gath  = null;

            // Run the string through so we can see if it'll fit on one line.  As a minor
            // optimization, we skip this step for "generic" strings, which are probably
            // the most common thing.
            if (dfd.FormatSubType != FormatDescriptor.SubType.None || highAscii)
            {
                gath = new StringGather(this, labelStr, "???", commentStr, delim,
                                        delim, StringGather.ByteStyle.CommaSep, MAX_OPERAND_LEN, true);
                FeedGath(gath, data, offset, dfd.Length, leadingBytes, showLeading,
                         trailingBytes, showTrailing);
                Debug.Assert(gath.NumLinesOutput > 0);
            }

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

            switch (dfd.FormatSubType)
            {
            case FormatDescriptor.SubType.None:
                // TODO(someday): something fancy with encodings to handle high-ASCII text?
                break;

            case FormatDescriptor.SubType.Dci:
            case FormatDescriptor.SubType.Reverse:
            case FormatDescriptor.SubType.DciReverse:
                // Fully configured above.
                break;

            case FormatDescriptor.SubType.CString:
                if (gath.NumLinesOutput == 1 && !gath.HasDelimiter)
                {
                    opcodeStr    = sDataOpNames.StrNullTerm;
                    showTrailing = false;
                }
                break;

            case FormatDescriptor.SubType.L8String:
                if (gath.NumLinesOutput == 1 && !gath.HasDelimiter)
                {
                    opcodeStr   = sDataOpNames.StrLen8;
                    showLeading = false;
                }
                break;

            case FormatDescriptor.SubType.L16String:
                // Implement as macro?
                break;

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

            if (highAscii)
            {
                OutputNoJoy(offset, dfd.Length, labelStr, commentStr);
                return;
            }

            // Create a new StringGather, with the final opcode choice.
            gath = new StringGather(this, labelStr, opcodeStr, commentStr, delim,
                                    delim, StringGather.ByteStyle.CommaSep, MAX_OPERAND_LEN, false);
            FeedGath(gath, data, offset, dfd.Length, leadingBytes, showLeading,
                     trailingBytes, showTrailing);
        }