/* * 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 { /// /// Common interface for generating assembler-specific source code. /// public interface IGenerator { /// /// 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. /// /// Table of pseudo-op names. /// Format configuration. void GetDefaultDisplayFormat(out PseudoOp.PseudoOpNames pseudoOps, out Formatter.FormatConfig formatConfig); /// /// Configure generator. Must be called before calling any other method or using /// properties, unless otherwise noted. /// /// Project to generate source for. /// Directory in which to create output files. /// Name to use as base for filenames. /// Version of assembler to target. Pass in null /// to target latest known version. /// App settings object. void Configure(DisasmProject project, string workDirectory, string fileNameBase, AssemblerVersion asmVersion, AppSettings settings); /// /// Project object with file data and Anattribs. /// DisasmProject Project { get; } /// /// Source code formatter. /// Formatter SourceFormatter { get; } /// /// Application settings. /// AppSettings Settings { get; } /// /// Assembler-specific behavior. Used to handle quirky behavior for things that /// are otherwise managed by common code. /// AssemblerQuirks Quirks { get; } /// /// Label localization object. Behavior is assembler-specific. /// LabelLocalizer Localizer { get; } /// /// 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). /// int StartOffset { get; } /// /// Generates source files on a background thread. Method must not make any UI calls. /// /// Async work object, used to report progress updates and /// check for cancellation. /// Object with list of pathnames of generated files. GenerationResults GenerateSource(BackgroundWorker worker); /// /// Provides an opportunity for the assembler to replace a mnemonic with another, or /// output an instruction as hex bytes. /// /// Opcode offset. /// Opcode to replace. /// 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. string ModifyOpcode(int offset, OpDef op); /// /// 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). /// /// Instruction offset. /// Existing descriptor. /// Operand value. /// Replacement format descriptor. If no changes are desired, returns /// the dfd argument. FormatDescriptor ModifyInstructionOperandFormat(int offset, FormatDescriptor dfd, int operand); /// /// Allows the generator to issue character encoding update instructions for source /// files with more than one encoding. /// /// /// This may be called for non-character numeric descriptors. /// /// Format descriptor for character or string. void UpdateCharacterEncoding(FormatDescriptor dfd); /// /// Generates an opcode/operand pair for a short sequence of bytes (1-4 bytes). /// Does not produce any source output. /// /// Offset to data. /// Number of bytes (1-4). /// Opcode mnemonic. /// Formatted operand. void GenerateShortSequence(int offset, int length, out string opcode, out string operand); /// /// 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. /// void OutputAsmConfig(); /// /// Outputs one or more lines of data for the specified offset. /// /// Offset to data. void OutputDataOp(int offset); /// /// Outputs an equate directive. The numeric value is already formatted. /// /// Symbol label. /// Formatted value. /// End-of-line comment. void OutputEquDirective(string name, string valueStr, string comment); /// /// Outputs a series of local variable definitions. /// /// Offset at which table is defined. /// New definitions, i.e. just the variables that were defined /// at this offset. /// All variable definitions that are active at this point. void OutputLocalVariableTable(int offset, List newDefs, LocalVariableTable allDefs); /// /// Outputs a code origin directive. /// /// Offset of code targeted to new address. /// 24-bit address. void OutputOrgDirective(int offset, int address); /// /// 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. /// /// Offset of change. /// Previous value for M flag. /// Previous value for X flag. /// New value for M flag. /// New value for X flag. void OutputRegWidthDirective(int offset, int prevM, int prevX, int newM, int newX); /// /// 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. /// /// Optional label. /// Opcode mnemonic. /// Operand; may be empty. /// Optional comment. void OutputLine(string label, string opcode, string operand, string comment); /// /// Output a line of source code. This will be output as-is. /// /// Full text of line to outut. void OutputLine(string fullLine); } /// /// Enumeration of quirky or buggy behavior that GenCommon needs to handle. /// public class AssemblerQuirks { /// /// Does the assembler expect the bit index for BBR/BBS/RMB/SMB to be expressed as /// a separate argument? /// public bool BitNumberIsArg { get; set; } /// /// Are 8-bit constant args to MVN/MVP output without a leading '#'? /// public bool BlockMoveArgsNoHash { get; set; } /// /// Are the arguments to MVN/MVP reversed? /// public bool BlockMoveArgsReversed { get; set; } /// /// Does a leading underscore in a label have a special meaning? (e.g. 64tass) /// public bool LeadingUnderscoreSpecial { get; set; } /// /// Do we need to specify a 24-bit value for 16-bit absolute arguments that are /// formed with the Program Bank Register (JMP/JSR)? /// public bool Need24BitsForAbsPBR { get; set; } /// /// Is the assembler unable to generate relative branches that wrap around banks? /// (Note this affects long-distance BRLs that don't appear to wrap.) /// public bool NoPcRelBankWrap { get; set; } /// /// Does the assembler support a type of label whose value can be redefined to /// act as a local variable? /// public bool NoRedefinableSymbols { get; set; } /// /// Is the assembler implemented as a single pass? (e.g. cc65) /// public bool SinglePassAssembler { get; set; } /// /// Is the assembler's label width determination performed only in the first pass, /// and not corrected when the actual width is determined? /// public bool SinglePassNoLabelCorrection { get; set; } /// /// Do 8-bit constant args to StackInt ops (BRK/COP) require a leading '#'? /// public bool StackIntOperandIsImmediate { get; set; } /// /// Does the assembler configure assembler widths based on SEP/REP, but doesn't /// track the emulation bit? /// public bool TracksSepRepNotEmu { get; set; } } /// /// Holds metadata generated by the assembly source generator. /// public class GenerationResults { public List PathNames { get; private set; } public string ExtraOptions { get; private set; } public GenerationResults(List pathNames, string extraOptions) { PathNames = CommonUtil.Container.CopyStringList(pathNames); ExtraOptions = extraOptions; } } }