mirror of
https://github.com/fadden/6502bench.git
synced 2024-12-01 22:50:35 +00:00
d3670c48e8
Correct handling of local variables. We now correctly uniquify them with regard to non-unique labels. Because local vars can effectively have global scope we mostly want to treat them as global, but they're uniquified relative to other globals very late in the process, so we can't just throw them in the symbol table and be done. Fortunately local variables exist in a separate namespace, so we just need to uniquify the variables relative to the post-localization symbol table. In other words, we take the symbol table, apply the label map, and rename any variable that clashes. This also fixes an older problem where we weren't masking the leading '_' on variable labels when generating 64tass output. The code list now makes non-unique labels obvious, but you can't tell the difference between unique global and unique local. What's more, the default type value in Edit Label is now adjusted to Global for unique locals that were auto-generated. To make it a bit easier to figure out what's what, the Info panel now has a "label type" line that reports the type. The 2023-non-unique-labels test had some additional tests added to exercise conflicts with local variables. The 2019-local-variables test output changed slightly because the de-duplicated variable naming convention was simplified.
253 lines
11 KiB
C#
253 lines
11 KiB
C#
/*
|
|
* Copyright 2019 faddenSoft
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
|
|
using Asm65;
|
|
|
|
namespace SourceGen.AsmGen {
|
|
/// <summary>
|
|
/// Common interface for generating assembler-specific source code.
|
|
/// </summary>
|
|
public interface IGenerator {
|
|
/// <summary>
|
|
/// Returns some strings and format options for use in for the display list, configurable
|
|
/// through the app settings "quick set" feature. These are not used when generating
|
|
/// source code.
|
|
///
|
|
/// This may be called on an unconfigured IGenerator, so this should not expect to
|
|
/// have access to project properties.
|
|
/// </summary>
|
|
/// <param name="pseudoOps">Table of pseudo-op names.</param>
|
|
/// <param name="formatConfig">Format configuration.</param>
|
|
void GetDefaultDisplayFormat(out PseudoOp.PseudoOpNames pseudoOps,
|
|
out Formatter.FormatConfig formatConfig);
|
|
|
|
|
|
/// <summary>
|
|
/// Configure generator. Must be called before calling any other method or using
|
|
/// properties, unless otherwise noted.
|
|
/// </summary>
|
|
/// <param name="project">Project to generate source for.</param>
|
|
/// <param name="workDirectory">Directory in which to create output files.</param>
|
|
/// <param name="fileNameBase">Name to use as base for filenames.</param>
|
|
/// <param name="asmVersion">Version of assembler to target. Pass in null
|
|
/// to target latest known version.</param>
|
|
/// <param name="settings">App settings object.</param>
|
|
void Configure(DisasmProject project, string workDirectory, string fileNameBase,
|
|
AssemblerVersion asmVersion, AppSettings settings);
|
|
|
|
/// <summary>
|
|
/// Project object with file data and Anattribs.
|
|
/// </summary>
|
|
DisasmProject Project { get; }
|
|
|
|
/// <summary>
|
|
/// Source code formatter.
|
|
/// </summary>
|
|
Formatter SourceFormatter { get; }
|
|
|
|
/// <summary>
|
|
/// Application settings.
|
|
/// </summary>
|
|
AppSettings Settings { get; }
|
|
|
|
/// <summary>
|
|
/// Assembler-specific behavior. Used to handle quirky behavior for things that
|
|
/// are otherwise managed by common code.
|
|
/// </summary>
|
|
AssemblerQuirks Quirks { get; }
|
|
|
|
/// <summary>
|
|
/// Label localization object. Behavior is assembler-specific.
|
|
/// </summary>
|
|
LabelLocalizer Localizer { get; }
|
|
|
|
/// <summary>
|
|
/// Generates source files on a background thread. Method must not make any UI calls.
|
|
/// </summary>
|
|
/// <param name="worker">Async work object, used to report progress updates and
|
|
/// check for cancellation.</param>
|
|
/// <returns>List of pathnames of generated files.</returns>
|
|
List<string> GenerateSource(BackgroundWorker worker);
|
|
|
|
/// <summary>
|
|
/// Provides an opportunity for the assembler to replace a mnemonic with another, or
|
|
/// output an instruction as hex bytes.
|
|
/// </summary>
|
|
/// <param name="offset">Opcode offset.</param>
|
|
/// <param name="op">Opcode to replace.</param>
|
|
/// <returns>Replacement mnemonic, an empty string if the original is fine, or
|
|
/// null if the op is unsupported or broken and should be emitted as hex.</returns>
|
|
string ModifyOpcode(int offset, OpDef op);
|
|
|
|
/// <summary>
|
|
/// Provides an opportunity for the assembler to replace an instruction's format
|
|
/// descriptor with another. Only called if the instruction is explicitly formatted
|
|
/// (i.e. has a non-null descriptor).
|
|
/// </summary>
|
|
/// <param name="offset">Instruction offset.</param>
|
|
/// <param name="dfd">Existing descriptor.</param>
|
|
/// <param name="operand">Operand value.</param>
|
|
/// <returns>Replacement format descriptor. If no changes are desired, returns
|
|
/// the dfd argument.</returns>
|
|
FormatDescriptor ModifyInstructionOperandFormat(int offset, FormatDescriptor dfd,
|
|
int operand);
|
|
|
|
/// <summary>
|
|
/// Allows the generator to issue character encoding update instructions for source
|
|
/// files with more than one encoding.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This may be called for non-character numeric descriptors.
|
|
/// </remarks>
|
|
/// <param name="dfd">Format descriptor for character or string.</param>
|
|
void UpdateCharacterEncoding(FormatDescriptor dfd);
|
|
|
|
/// <summary>
|
|
/// Generates an opcode/operand pair for a short sequence of bytes (1-4 bytes).
|
|
/// Does not produce any source output.
|
|
/// </summary>
|
|
/// <param name="offset">Offset to data.</param>
|
|
/// <param name="count">Number of bytes (1-4).</param>
|
|
/// <param name="opcode">Opcode mnemonic.</param>
|
|
/// <param name="operand">Formatted operand.</param>
|
|
void GenerateShortSequence(int offset, int length, out string opcode, out string operand);
|
|
|
|
/// <summary>
|
|
/// Outputs zero or more lines of assembler configuration. This comes after the
|
|
/// header comment but before any directives. Useful for configuring the CPU type
|
|
/// and assembler options.
|
|
/// </summary>
|
|
void OutputAsmConfig();
|
|
|
|
/// <summary>
|
|
/// Outputs one or more lines of data for the specified offset.
|
|
/// </summary>
|
|
/// <param name="offset">Offset to data.</param>
|
|
void OutputDataOp(int offset);
|
|
|
|
/// <summary>
|
|
/// Outputs an equate directive. The numeric value is already formatted.
|
|
/// </summary>
|
|
/// <param name="name">Symbol label.</param>
|
|
/// <param name="valueStr">Formatted value.</param>
|
|
/// <param name="comment">End-of-line comment.</param>
|
|
void OutputEquDirective(string name, string valueStr, string comment);
|
|
|
|
/// <summary>
|
|
/// Outputs a series of local variable definitions.
|
|
/// </summary>
|
|
/// <param name="offset">Offset at which table is defined.</param>
|
|
/// <param name="newDefs">New definitions, i.e. just the variables that were defined
|
|
/// at this offset.</param>
|
|
/// <param name="allDefs">All variable definitions that are active at this point.</param>
|
|
void OutputLocalVariableTable(int offset, List<DefSymbol> newDefs,
|
|
LocalVariableTable allDefs);
|
|
|
|
/// <summary>
|
|
/// Outputs a code origin directive.
|
|
/// </summary>
|
|
/// <param name="offset">Offset of code targeted to new address.</param>
|
|
/// <param name="address">24-bit address.</param>
|
|
void OutputOrgDirective(int offset, int address);
|
|
|
|
/// <summary>
|
|
/// Notify the assembler of a change in register width.
|
|
///
|
|
/// Merlin32 always sets both values (e.g. "MX %00"), cc65 sets each register
|
|
/// individually (".A16", ".I8"). We need to accommodate both styles.
|
|
/// </summary>
|
|
/// <param name="offset">Offset of change.</param>
|
|
/// <param name="prevM">Previous value for M flag.</param>
|
|
/// <param name="prevX">Previous value for X flag.</param>
|
|
/// <param name="newM">New value for M flag.</param>
|
|
/// <param name="newX">New value for X flag.</param>
|
|
void OutputRegWidthDirective(int offset, int prevM, int prevX, int newM, int newX);
|
|
|
|
/// <summary>
|
|
/// Output a line of source code. All elements must be fully formatted, except for
|
|
/// certain assembler-specific things like ':' on labels. The items will be padded
|
|
/// with spaces to fit specific column widths.
|
|
/// </summary>
|
|
/// <param name="label">Optional label.</param>
|
|
/// <param name="opcode">Opcode mnemonic.</param>
|
|
/// <param name="operand">Operand; may be empty.</param>
|
|
/// <param name="comment">Optional comment.</param>
|
|
void OutputLine(string label, string opcode, string operand, string comment);
|
|
|
|
/// <summary>
|
|
/// Output a line of source code. This will be output as-is.
|
|
/// </summary>
|
|
/// <param name="fullLine">Full text of line to outut.</param>
|
|
void OutputLine(string fullLine);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enumeration of quirky or buggy behavior that GenCommon needs to handle.
|
|
/// </summary>
|
|
public class AssemblerQuirks {
|
|
/// <summary>
|
|
/// Does a leading underscore in a label have a special meaning? (e.g. 64tass)
|
|
/// </summary>
|
|
public bool LeadingUnderscoreSpecial { get; set; }
|
|
|
|
/// <summary>
|
|
/// Are 8-bit constant args to MVN/MVP output without a leading '#'?
|
|
/// </summary>
|
|
public bool BlockMoveArgsNoHash { get; set; }
|
|
|
|
/// <summary>
|
|
/// Are the arguments to MVN/MVP reversed?
|
|
/// </summary>
|
|
public bool BlockMoveArgsReversed { get; set; }
|
|
|
|
/// <summary>
|
|
/// Does the assembler support a type of label whose value can be redefined to
|
|
/// act as a local variable?
|
|
/// </summary>
|
|
public bool NoRedefinableSymbols { get; set; }
|
|
|
|
/// <summary>
|
|
/// Is the assembler unable to generate relative branches that wrap around banks?
|
|
/// (Note this affects long-distance BRLs that don't appear to wrap.)
|
|
/// </summary>
|
|
public bool NoPcRelBankWrap { get; set; }
|
|
|
|
/// <summary>
|
|
/// Do 8-bit constant args to StackInt ops (BRK/COP) require a leading '#'?
|
|
/// </summary>
|
|
public bool StackIntOperandIsImmediate { get; set; }
|
|
|
|
/// <summary>
|
|
/// Is the assembler implemented as a single pass? (e.g. cc65)
|
|
/// </summary>
|
|
public bool SinglePassAssembler { get; set; }
|
|
|
|
/// <summary>
|
|
/// Is the assembler's label width determination performed only in the first pass,
|
|
/// and not corrected when the actual width is determined?
|
|
/// </summary>
|
|
public bool SinglePassNoLabelCorrection { get; set; }
|
|
|
|
/// <summary>
|
|
/// Does the assembler configure assembler widths based on SEP/REP, but doesn't
|
|
/// track the emulation bit?
|
|
/// </summary>
|
|
public bool TracksSepRepNotEmu { get; set; }
|
|
}
|
|
} |