Beispiel #1
0
        // IGenerator
        public void OutputArDirective(CommonUtil.AddressMap.AddressChange change)
        {
            // This is similar in operation to the AsmTass64 implementation.  See comments there.
            Debug.Assert(mPcDepth >= 0);
            int nextAddress = change.Address;

            if (nextAddress == Address.NON_ADDR)
            {
                // Start non-addressable regions at zero to ensure they don't overflow bank.
                nextAddress = 0;
            }
            if (change.IsStart)
            {
                if (change.Region.HasValidPreLabel)
                {
                    string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel);
                    OutputLine(labelStr, string.Empty, string.Empty, string.Empty);
                }
                if (mPcDepth == 0 && mFirstIsOpen)
                {
                    mPcDepth++;

                    // Set the "real" PC for the first address change.  If we're in "loadable"
                    // mode, just set "*=".  If we're in "streaming" mode, we set "*=" to zero
                    // and then use a pseudo-PC.
                    if (mOutputMode == OutputMode.Loadable)
                    {
                        OutputLine("*", "=", SourceFormatter.FormatHexValue(nextAddress, 4),
                                   string.Empty);
                        return;
                    }
                    else
                    {
                        // set the real PC to address zero to ensure we get a full 64KB
                        OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty);
                    }
                }
                AddressMap.AddressRegion region = change.Region;
                string addrStr;
                if (region.HasValidIsRelative)
                {
                    int    diff = nextAddress - region.PreLabelAddress;
                    string pfxStr;
                    if (diff >= 0)
                    {
                        pfxStr = "*+";
                    }
                    else
                    {
                        pfxStr = "*-";
                        diff   = -diff;
                    }
                    addrStr = pfxStr + SourceFormatter.FormatHexValue(diff, 4);
                }
                else
                {
                    addrStr = SourceFormatter.FormatHexValue(nextAddress, 4);
                }
                OutputLine(string.Empty,
                           SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
                           addrStr + " {",
                           string.Empty);
                mPcDepth++;
            }
            else
            {
                mPcDepth--;
                if (mPcDepth > 0 || !mFirstIsOpen)
                {
                    // close previous block
                    OutputLine(string.Empty,
                               SourceFormatter.FormatPseudoOp(sDataOpNames.ArEndDirective),
                               string.Empty, string.Empty);
                    //";" + SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective));
                }
                else
                {
                    // mark initial "*=" region as closed, but don't output anything
                    mFirstIsOpen = false;
                }
            }
        }
Beispiel #2
0
        // IGenerator
        public void OutputArDirective(CommonUtil.AddressMap.AddressChange change)
        {
            // 64tass separates the "compile offset", which determines where the output fits
            // into the generated binary, and "program counter", which determines the code
            // the assembler generates.  Since we need to explicitly specify every byte in
            // the output file, having a distinct compile offset isn't useful here.  We want
            // to set it once before the first line of code, then leave it alone.
            //
            // Any subsequent ORG changes are made to the program counter, and take the form
            // of a pair of ops (".logical <addr>" to open, ".here" to end).  Omitting the .here
            // causes an error.
            //
            // If this is a "streamable" file, meaning it won't actually load into 64K of RAM
            // without wrapping around, then we skip the "* = addr" (same as "* = 0") and just
            // start with ".logical" segments.
            //
            // The assembler's approach is best represented by having an address region that
            // spans the entire file, with one or more "logical" regions inside.  In practice
            // (especially for multi-bank 65816 code) that may not be the case, but the
            // assembler is still expecting us to start with a "* =" and then fit everything
            // inside that.  So we treat the first region specially, whether or not it wraps
            // the rest of the file.
            Debug.Assert(mPcDepth >= 0);
            int nextAddress = change.Address;

            if (nextAddress == Address.NON_ADDR)
            {
                // Start non-addressable regions at zero to ensure they don't overflow bank.
                nextAddress = 0;
            }
            if (change.IsStart)
            {
                if (change.Region.HasValidPreLabel)
                {
                    string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel);
                    OutputLine(labelStr, string.Empty, string.Empty, string.Empty);
                }
                if (mPcDepth == 0 && mFirstIsOpen)
                {
                    mPcDepth++;

                    // Set the "real" PC for the first address change.  If we're in "loadable"
                    // mode, just set "*=".  If we're in "streaming" mode, we set "*=" to zero
                    // and then use a pseudo-PC.
                    if (mOutputMode == OutputMode.Loadable)
                    {
                        OutputLine("*", "=",
                                   SourceFormatter.FormatHexValue(nextAddress, 4), string.Empty);
                        return;
                    }
                    else
                    {
                        // Set the real PC to address zero to ensure we get a full 64KB.  The
                        // assembler assumes this as a default, so it can be omitted.
                        //OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty);
                    }
                }

                AddressMap.AddressRegion region = change.Region;
                string addrStr;
                if (region.HasValidIsRelative)
                {
                    int    diff = nextAddress - region.PreLabelAddress;
                    string pfxStr;
                    if (diff >= 0)
                    {
                        pfxStr = "*+";
                    }
                    else
                    {
                        pfxStr = "*-";
                        diff   = -diff;
                    }
                    addrStr = pfxStr + SourceFormatter.FormatHexValue(diff, 4);
                }
                else
                {
                    addrStr = SourceFormatter.FormatHexValue(nextAddress, 4);
                }
                OutputLine(string.Empty,
                           SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
                           addrStr,
                           string.Empty);
                mPcDepth++;
            }
            else
            {
                mPcDepth--;
                if (mPcDepth > 0 || !mFirstIsOpen)
                {
                    // close previous block
                    OutputLine(string.Empty,
                               SourceFormatter.FormatPseudoOp(sDataOpNames.ArEndDirective),
                               string.Empty, string.Empty);
                }
                else
                {
                    // mark initial "*=" region as closed, but don't output anything
                    mFirstIsOpen = false;
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Generates the initial mGlobalFlags and mGlobalLabels lists, as well as the
        /// full cross-reference pair list.
        /// </summary>
        private void GenerateLists()
        {
            // For every offset that has a label, add an entry to the source/target pair list
            // for every offset that references it.
            //
            // If the label isn't marked as "local or global", add it to the global-label list.
            //
            // The first label encountered is always treated as global.  Note it may not appear
            // at offset zero.

            bool first = true;

            for (int offset = 0; offset < mProject.FileDataLength; offset++)
            {
                // Find all user labels and auto labels.
                Symbol sym = mProject.GetAnattrib(offset).Symbol;

                // In cc65, variable declarations end the local label scope.  We insert a
                // fake global symbol if we encounter a table with a nonzero number of entries.
                if (QuirkVariablesEndScope &&
                    mProject.LvTables.TryGetValue(offset, out LocalVariableTable value) &&
                    value.Count > 0)
                {
                    mGlobalFlags[offset] = true;
                    mGlobalLabels.Add(new OffsetLabel(offset, "!VARTAB!"));
                    continue;
                }
                if (sym == null)
                {
                    // No label at this offset.
                    continue;
                }

                if (first || !sym.CanBeLocal)
                {
                    first = false;
                    mGlobalFlags[offset] = true;
                    mGlobalLabels.Add(new OffsetLabel(offset, sym.Label));

                    // Don't add to pairs list.
                    continue;
                }

                // If nothing actually references this label, the xref set will be empty.
                XrefSet xrefs = mProject.GetXrefSet(offset);
                if (xrefs != null)
                {
                    foreach (XrefSet.Xref xref in xrefs)
                    {
                        if (!xref.IsByName)
                        {
                            continue;
                        }

                        mOffsetPairs.Add(new OffsetPair(xref.Offset, offset));
                    }
                }
            }

            // Add the AddressRegion pre-labels to the list, as globals.  We may need to
            // re-map the label if it matches a mnemonic.
            IEnumerator <CommonUtil.AddressMap.AddressChange> addrIter =
                mProject.AddrMap.AddressChangeIterator;

            while (addrIter.MoveNext())
            {
                CommonUtil.AddressMap.AddressChange change = addrIter.Current;
                if (!change.IsStart || !change.Region.HasValidPreLabel)
                {
                    continue;
                }
                mGlobalFlags[change.Region.Offset] = true;
                mGlobalLabels.Add(new OffsetLabel(change.Region.Offset, change.Region.PreLabel));
            }
        }
Beispiel #4
0
        /// <summary>
        /// Analyzes labels to identify which ones may be treated as non-global.
        /// </summary>
        public void Analyze()
        {
            Debug.Assert(LocalPrefix.Length > 0);

            // Init global flags list.  An entry is set if the associated offset has a global
            // label.  It will be false if the entry has a local label, or no label.
            mGlobalFlags.SetAll(false);

            // Currently we only support the "local labels have scope that ends at a global
            // label" variety.  The basic idea is to start by assuming that everything not
            // explicitly marked global is local, and then identify situations like this:
            //
            //           lda :local
            //   global  eor #$ff
            //   :local  sta $00
            //
            // The reference crosses a global label, so the "target" label must be made global.
            // This can have ripple effects, so we have to iterate.  Note it doesn't matter
            // whether "global" is referenced anywhere.
            //
            // The current algorithm uses a straightforward O(n^2) approach.

            //
            // Step 1: generate source/target pairs and global label list
            //
            GenerateLists();

            //
            // Step 2: walk through the list of global symbols, identifying source/target
            // pairs that cross them.  If a pair matches, the target label is added to the
            // end of the mGlobalLabels list, and removed from the pair list.
            //
            // When we're done, mGlobalFlags[] will identify the offsets with global labels.
            //
            for (int index = 0; index < mGlobalLabels.Count; index++)
            {
                FindIntersectingPairs(mGlobalLabels[index]);
            }

            // We're done with these.  Take out the trash.
            mGlobalLabels.Clear();
            mOffsetPairs.Clear();

            //
            // Step 3: remap global labels.  There are three reasons we might need to do this:
            //  (1) It has a leading underscore AND LocalPrefix is '_'.
            //  (2) The label matches an opcode mnemonic (case-insensitive) AND NoOpcodeMnemonics
            //      is set.
            //  (3) It's a non-unique local that got promoted to global.
            //
            // In each case we need to modify the label to meet the assembler requirements, and
            // then modify the label until it's unique.
            //
            LabelMap = new Dictionary <string, string>();
            Dictionary <string, string> allGlobalLabels = new Dictionary <string, string>();
            bool remapUnders = (LocalPrefix == "_");

            HashSet <string> rsvdNames = new HashSet <string>();

            if (QuirkNoOpcodeMnemonics)
            {
                // Create a searchable list of opcode names using the current CPU definition.
                // (All tested assemblers that failed on opcode names only did so for names
                // that were part of the current definition, e.g. "TSB" was accepted as a label
                // when the CPU was set to 6502.)
                Asm65.CpuDef cpuDef = mProject.CpuDef;
                for (int i = 0; i < 256; i++)
                {
                    Asm65.OpDef op = cpuDef.GetOpDef(i);
                    // There may be multiple entries with the same name (e.g. "NOP").  That's fine.
                    rsvdNames.Add(op.Mnemonic.ToUpperInvariant());
                }
            }

            // Add any words that the assembler just doesn't like.
            if (ReservedWords != null)
            {
                foreach (string str in ReservedWords)
                {
                    rsvdNames.Add(str);
                }
            }

            for (int i = 0; i < mProject.FileDataLength; i++)
            {
                if (!mGlobalFlags[i])
                {
                    continue;
                }
                Symbol sym = mProject.GetAnattrib(i).Symbol;
                if (sym == null)
                {
                    // Should only happen when we insert a dummy global label for the
                    // "variables end scope" quirk.
                    continue;
                }

                RemapGlobalSymbol(sym, allGlobalLabels, rsvdNames, remapUnders);
            }

            // Remap any project/platform symbols that clash with opcode mnemonics or have
            // leading underscores that aren't allowed.
            foreach (DefSymbol defSym in mProject.ActiveDefSymbolList)
            {
                //if (opNames != null && opNames.ContainsKey(defSym.Label.ToUpperInvariant())) {
                //    // Clashed with mnemonic.  Uniquify it.
                //    Debug.WriteLine("Renaming clashing def sym: " + defSym.Label);
                //    string newLabel = MakeUnique(defSym.Label, allGlobalLabels);
                //    LabelMap[defSym.Label] = newLabel;
                //    allGlobalLabels.Add(newLabel, newLabel);
                //}
                RemapGlobalSymbol(defSym, allGlobalLabels, rsvdNames, remapUnders);
            }

            // Remap any address region pre-labels with inappropriate values.
            IEnumerator <CommonUtil.AddressMap.AddressChange> addrIter =
                mProject.AddrMap.AddressChangeIterator;

            while (addrIter.MoveNext())
            {
                CommonUtil.AddressMap.AddressChange change = addrIter.Current;
                if (!change.IsStart || !change.Region.HasValidPreLabel)
                {
                    continue;
                }
                Symbol sym = new Symbol(change.Region.PreLabel, change.Region.PreLabelAddress,
                                        Symbol.Source.AddrPreLabel, Symbol.Type.ExternalAddr,
                                        Symbol.LabelAnnotation.None);
                RemapGlobalSymbol(sym, allGlobalLabels, rsvdNames, remapUnders);
            }

            //
            // Step 4: remap local labels.  There are two operations here.
            //
            // For each pair of global labels that have locals between them, we need to walk
            // through the locals and confirm that they don't clash with each other.  If they
            // do, we need to uniquify them within the local scope.  (This is only an issue
            // for non-unique locals.)
            //
            // Once a unique name has been found, we add an entry to LabelMap that has the
            // label with the LocalPrefix and without the non-unique tag.
            //
            // We also need to deal with symbols with a leading underscore when
            // LocalPrefix is '_'.
            //
            int startGlobal = -1;
            int numLocals   = 0;

            // Allocate a Dictionary here and pass it through so we don't have to allocate
            // a new one each time.
            Dictionary <string, string> scopedLocals = new Dictionary <string, string>();

            for (int i = 0; i < mProject.FileDataLength; i++)
            {
                if (mGlobalFlags[i])
                {
                    if (startGlobal < 0)
                    {
                        // very first one
                        startGlobal = i;
                        continue;
                    }
                    else if (numLocals > 0)
                    {
                        // There were locals following the previous global.  Process them.
                        ProcessLocals(startGlobal, i, scopedLocals);
                        startGlobal = i;
                        numLocals   = 0;
                    }
                    else
                    {
                        // Two adjacent globals.
                        startGlobal = i;
                    }
                }
                else
                {
                    // Not a global.  Is there a local symbol here?
                    Symbol sym = mProject.GetAnattrib(i).Symbol;
                    if (sym != null)
                    {
                        numLocals++;
                    }
                }
            }
            if (numLocals != 0)
            {
                // do the last bit
                ProcessLocals(startGlobal, mProject.FileDataLength, scopedLocals);
            }
        }
Beispiel #5
0
        // IGenerator
        public void OutputArDirective(CommonUtil.AddressMap.AddressChange change)
        {
            int nextAddress = change.Address;

            if (nextAddress == Address.NON_ADDR)
            {
                // Start non-addressable regions at zero to ensure they don't overflow bank.
                nextAddress = 0;
            }

            if (change.IsStart)
            {
                AddressMap.AddressRegion region = change.Region;
                if (region.HasValidPreLabel || region.HasValidIsRelative)
                {
                    // Need to output the previous ORG, if one is pending.
                    if (mNextAddress >= 0)
                    {
                        OutputLine(string.Empty,
                                   SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
                                   SourceFormatter.FormatHexValue(mNextAddress, 4),
                                   string.Empty);
                    }
                }
                if (region.HasValidPreLabel)
                {
                    string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel);
                    OutputLine(labelStr, string.Empty, string.Empty, string.Empty);
                }
                if (region.HasValidIsRelative)
                {
                    // Found a valid IsRelative.  Switch to "relative mode" if not there already.
                    mIsInRelative = true;
                }
                if (mIsInRelative)
                {
                    // Once we see a region with IsRelative set, we output regions as we
                    // find them until the next Flush.
                    string addrStr;
                    if (region.HasValidIsRelative)
                    {
                        int    diff = nextAddress - region.PreLabelAddress;
                        string pfxStr;
                        if (diff >= 0)
                        {
                            pfxStr = "*+";
                        }
                        else
                        {
                            pfxStr = "*-";
                            diff   = -diff;
                        }
                        addrStr = pfxStr + SourceFormatter.FormatHexValue(diff, 4);
                    }
                    else
                    {
                        addrStr = SourceFormatter.FormatHexValue(nextAddress, 4);
                    }
                    OutputLine(string.Empty,
                               SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective),
                               addrStr, string.Empty);

                    mNextAddress = -1;
                    return;
                }
            }

            mNextAddress = nextAddress;
        }
Beispiel #6
0
        /// <summary>
        /// Formats the address map for viewing.
        /// </summary>
        public static string GenerateString(DisasmProject project, Formatter formatter)
        {
            AddressMap addrMap  = project.AddrMap;
            bool       showBank = !project.CpuDef.HasAddr16;

            StringBuilder sb            = new StringBuilder();
            int           depth         = 0;
            int           prevOffset    = -1;
            int           prevAddr      = 0;
            int           lastEndOffset = -1;

            sb.AppendLine("Address region map for \"" + project.DataFileName + "\"");
            sb.Append(CRLF);

            IEnumerator <AddressChange> iter = addrMap.AddressChangeIterator;

            while (iter.MoveNext())
            {
                AddressChange change = iter.Current;
                if (change.IsStart)
                {
                    //if (change.Offset == lastEndOffset) {
                    //    // Extra vertical space for a START following an END at the same offset.
                    //    PrintDepthLines(sb, depth, true);
                    //    sb.Append(CRLF);
                    //    lastEndOffset = -1;
                    //}

                    if (prevOffset >= 0 && change.Offset != prevOffset)
                    {
                        // Start of region at new offset.  Output address info for space
                        // between previous start or end.
                        PrintAddressInfo(sb, formatter, depth, prevAddr,
                                         change.Offset - prevOffset, showBank);
                    }

                    // Start following end, or start following start after a gap.
                    sb.Append(formatter.FormatOffset24(change.Offset));
                    PrintDepthLines(sb, depth, false);
                    sb.Append("+- " + "start");

                    if (change.IsSynthetic)
                    {
                        sb.Append(" (auto-generated)");
                    }
                    else
                    {
                        // If there's a label here, show it.
                        Anattrib attr = project.GetAnattrib(change.Offset);
                        if (attr.Symbol != null && !string.IsNullOrEmpty(attr.Symbol.Label))
                        {
                            sb.Append(" '");
                            sb.Append(attr.Symbol.GenerateDisplayLabel(formatter));
                            sb.Append("'");
                        }
                    }
                    if (change.Region.HasValidPreLabel)
                    {
                        sb.Append("  pre='");
                        sb.Append(change.Region.PreLabel);
                        sb.Append("'");
                    }

                    sb.Append(CRLF);

                    prevOffset = change.Offset;
                    prevAddr   = change.Address;
                    depth++;
                }
                else
                {
                    Debug.Assert(prevOffset >= 0);
                    depth--;

                    if (change.Offset + 1 != prevOffset)
                    {
                        // End of region at new offset.  Output address info for space
                        // between previous start or end.
                        PrintAddressInfo(sb, formatter, depth + 1, prevAddr,
                                         change.Offset + 1 - prevOffset, showBank);
                    }

                    sb.Append(formatter.FormatOffset24(change.Offset));
                    PrintDepthLines(sb, depth, false);
                    sb.Append("+- " + "end");
                    if (change.Region.IsFloating)
                    {
                        sb.Append(" (floating)");
                    }
                    //PrintAddress(sb, formatter, change.Address, showBank);
                    //sb.Append(")");
                    sb.Append(CRLF);

                    // Add a blank line, but with the depth lines.
                    if (depth > 0)
                    {
                        PrintDepthLines(sb, depth, true);
                    }
                    sb.Append(CRLF);

                    // Use offset+1 here so it lines up with start records.
                    prevOffset = lastEndOffset = change.Offset + 1;
                    prevAddr   = change.Address;
                }
            }
            Debug.Assert(depth == 0);

            return(sb.ToString());
        }