예제 #1
0
        /// <summary>
        /// Remaps labels that match opcode names.  Updated names will be added to LabelMap.
        /// This should be run after localization and underscore concealment have finished.
        /// </summary>
        /// <remarks>
        /// Most assemblers don't like it if you create a label with the same name as an
        /// opcode, e.g. "jmp LSR" doesn't work.  We can use the label map to work around
        /// the issue.
        ///
        /// Most assemblers regard mnemonics as case-insensitive, even if labels are
        /// case-sensitive, so we want to remap both "lsr" and "LSR".
        ///
        /// This doesn't really have anything to do with label localization other than that
        /// we're updating the label remap table.
        /// </remarks>
        public void FixOpcodeLabels()
        {
            if (LabelMap == null)
            {
                LabelMap = new Dictionary <string, string>();
            }

            // 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.)
            Dictionary <string, Asm65.OpDef> opnames = new Dictionary <string, Asm65.OpDef>();

            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.
                opnames[op.Mnemonic.ToUpperInvariant()] = op;
            }

            // Create a list of all labels, for uniqueness testing.  If a label has been
            // remapped, we add the remapped entry.
            // (All tested assemblers that failed on opcode names only did so for names
            // in their non-localized form.  While "LSR" failed, "@LSR", "_LSR", ".LSR", etc.
            // were accepted.  So if it  was remapped by the localizer, we don't need to
            // worry about it.)
            SortedList <string, string> allLabels = new SortedList <string, string>();

            for (int i = 0; i < mProject.FileDataLength; i++)
            {
                Symbol sym = mProject.GetAnattrib(i).Symbol;
                if (sym == null)
                {
                    continue;
                }
                LabelMap.TryGetValue(sym.Label, out string mapLabel);
                if (mapLabel != null)
                {
                    allLabels.Add(mapLabel, mapLabel);
                }
                else
                {
                    allLabels.Add(sym.Label, sym.Label);
                }
            }

            // Now run through the list of labels, looking for any that match opcode
            // mnemonics.
            for (int i = 0; i < mProject.FileDataLength; i++)
            {
                Symbol sym = mProject.GetAnattrib(i).Symbol;
                if (sym == null)
                {
                    // No label at this offset.
                    continue;
                }
                string cmpLabel = sym.Label;
                if (LabelMap.TryGetValue(sym.Label, out string mapLabel))
                {
                    cmpLabel = mapLabel;
                }

                if (opnames.ContainsKey(cmpLabel.ToUpperInvariant()))
                {
                    //Debug.WriteLine("Remapping label (op mnemonic): " + sym.Label);

                    int    uval = 0;
                    string uniqueLabel;
                    do
                    {
                        uval++;
                        uniqueLabel = cmpLabel + "_" + uval.ToString();
                    } while (allLabels.ContainsKey(uniqueLabel));

                    allLabels.Add(uniqueLabel, uniqueLabel);
                    LabelMap.Add(sym.Label, uniqueLabel);
                }
            }

            if (LabelMap.Count == 0)
            {
                // didn't do anything, lose the table
                LabelMap = null;
            }
        }
예제 #2
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);
            }
        }