1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-11-12 06:06:12 +00:00
6502bench/SourceGen/AsmGen/IGenerator.cs
Andy McFadden e6c5c7f8df ORG rework, part 6
Added support for non-addressable regions, which are useful for things
like file headers stripped out by the system loader, or chunks that
get loaded into non-addressable graphics RAM.  Regions are specified
with the "NA" address value.  The code list displays the address field
greyed out, starting from zero (which is kind of handy if you want to
know the relative offset within the region).

Putting labels in non-addressable regions doesn't make sense, but
symbol resolution is complicated enough that we really only have two
options: ignore the labels entirely, or allow them but warn of their
presence.  The problem isn't so much the label, which you could
legitimately want to access from an extension script, but rather the
references to them from code or data.  So we keep the label and add a
warning to the Messages list when we see a reference.

Moved NON_ADDR constants to Address class.  AddressMap now has a copy.
This is awkward because Asm65 and CommonUtil don't share.

Updated the asm code generators to understand NON_ADDR, and reworked
the API so that Merlin and cc65 output is correct for nested regions.

Address region changes are now noted in the anattribs array, which
makes certain operations faster than checking the address map.  It
also fixes a failure to recognize mid-instruction region changes in
the code analyzer.

Tweaked handling of synthetic regions, which are non-addressable areas
generated by the linear address map traversal to fill in any "holes".
The address region editor now treats attempts to edit them as
creation of a new region.
2021-09-30 21:11:26 -07:00

297 lines
12 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>
/// File offset to start generating code from, usually zero. Will be nonzero for files
/// with a header that is supposed to be generated by the assembler (e.g. C64 PRG).
/// </summary>
int StartOffset { 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>Object with list of pathnames of generated files.</returns>
GenerationResults 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 an address region directive.
/// </summary>
/// <param name="isStart">True if this is the start of a region.</param>
/// <param name="change">Address map change record.</param>
void OutputArDirective(CommonUtil.AddressMap.AddressChange change);
/// <summary>
/// Signals the code generator to flush any pending address region directives. Useful
/// for generation of non-hierarchical directives.
/// </summary>
void FlushArDirectives();
/// <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 the assembler require a qualifier to be added to the operand when an instruction
/// formed with the Program Bank Register (JMP/JSR) attempts to access a bank zero
/// address from outside bank zero?
/// </summary>
public bool BankZeroAbsPBRRestrict { get; set; }
/// <summary>
/// Does the assembler expect the bit index for BBR/BBS/RMB/SMB to be expressed as
/// a separate argument?
/// </summary>
public bool BitNumberIsArg { 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 a leading underscore in a label have a special meaning? (e.g. 64tass)
/// </summary>
public bool LeadingUnderscoreSpecial { get; set; }
/// <summary>
/// Do we need to specify a 24-bit value for 16-bit absolute arguments that are
/// formed with the Program Bank Register (JMP/JSR)?
/// </summary>
public bool Need24BitsForAbsPBR { 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>
/// 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 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>
/// Do 8-bit constant args to StackInt ops (BRK/COP) require a leading '#'?
/// </summary>
public bool StackIntOperandIsImmediate { 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; }
}
/// <summary>
/// Holds metadata generated by the assembly source generator.
/// </summary>
public class GenerationResults {
public List<string> PathNames { get; private set; }
public string ExtraOptions { get; private set; }
public GenerationResults(List<string> pathNames, string extraOptions) {
PathNames = CommonUtil.Container.CopyStringList(pathNames);
ExtraOptions = extraOptions;
}
}
}