diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..30f0112 --- /dev/null +++ b/.gitignore @@ -0,0 +1,312 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# 6502bench-specific stuff +SourceGen/SourceGen-settings +SourceGen/PluginDll +DIST_Debug +DIST_release + + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ diff --git a/Asm65/Address.cs b/Asm65/Address.cs new file mode 100644 index 0000000..204ca50 --- /dev/null +++ b/Asm65/Address.cs @@ -0,0 +1,92 @@ +/* + * Copyright 2018 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.Diagnostics; + +namespace Asm65 { + public static class Address { + /// + /// Converts a 16- or 24-bit address to a string. + /// + /// + /// + public static string AddressToString(int addr, bool always24) { + if (!always24 && addr < 65536) { + return addr.ToString("x4"); + } else { + return (addr >> 16).ToString("x2") + "/" + (addr & 0xffff).ToString("x4"); + } + } + + /// + /// Parses and validates a 16- or 24-bit address, expressed in hexadecimal. Bits + /// 16-23 may be specified with a slash. + /// + /// The following all evaluate to the same thing: 1000, $1000, 0x1000, 00/1000. + /// + /// String to validate. + /// Maximum valid address value. + /// Integer form. + /// True if the address is valid. + public static bool ParseAddress(string addrStr, int max, out int addr) { + string trimStr = addrStr.Trim(); // strip whitespace + if (trimStr.Length < 1) { + addr = -1; + return false; + } + if (trimStr[0] == '$') { + trimStr = trimStr.Remove(0, 1); + } + + int slashIndex = trimStr.IndexOf('/'); + try { + if (slashIndex < 0) { + addr = Convert.ToInt32(trimStr, 16); + if (addr < 0 || addr > max) { + Debug.WriteLine("Simple value out of range"); + addr = -1; + return false; + } + } else { + string[] splitStr = trimStr.Split('/'); + if (splitStr.Length == 2) { + int addr1 = Convert.ToInt32(splitStr[0], 16); + int addr2 = Convert.ToInt32(splitStr[1], 16); + addr = (addr1 << 16) | addr2; + // Check components separately to catch overflow. + if (addr1 < 0 || addr1 > 255 || addr2 < 0 || addr2 > 65535 || + addr > max) { + Debug.WriteLine("Slash value out of range"); + addr = -1; + return false; + } + } else { + addr = -1; + } + } + } catch (Exception) { + // Thrown from Convert.ToInt32 + //Debug.WriteLine("ValidateAddress: conversion of '" + addrStr + "' failed: " + + // ex.Message); + addr = -1; + return false; + } + + //Debug.WriteLine("Conv " + addrStr + " --> " + addr.ToString("x6")); + return true; + } + } +} diff --git a/Asm65/Asm65.csproj b/Asm65/Asm65.csproj new file mode 100644 index 0000000..7556a05 --- /dev/null +++ b/Asm65/Asm65.csproj @@ -0,0 +1,26 @@ + + + + netstandard2.0 + + + + + + + + + True + True + Resources.resx + + + + + + PublicResXFileCodeGenerator + Resources.Designer.cs + + + + diff --git a/Asm65/CpuDef.cs b/Asm65/CpuDef.cs new file mode 100644 index 0000000..0d6d20d --- /dev/null +++ b/Asm65/CpuDef.cs @@ -0,0 +1,1184 @@ +/* + * Copyright 2018 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.Diagnostics; + +namespace Asm65 { + /// + /// CPU definition. Includes a set of 256 opcodes. + /// + public class CpuDef { + /// + /// Human-readable name. + /// + public string Name { get; private set; } + + /// + /// Maximum possible address. + /// + public int MaxAddressValue { get; private set; } + + /// + /// Does this CPU have a 16-bit address space? + /// + public bool HasAddr16 { get { return MaxAddressValue <= 0xffff; } } + + /// + /// Does this CPU support the emulation flag (65802/65816)? + /// + public bool HasEmuFlag { get; private set; } + + /// + /// CPU type value. + /// + public CpuType Type { get; private set; } + + /// + /// True if undocumented opcodes are included. (This does not mean that the CPU has + /// undocumented opcodes, only that they haven't been stripped out if they exist.) + /// + public bool HasUndocumented { get; private set; } + + /// + /// Instruction set, 256 entries. + /// + private OpDef[] mOpDefs; + + /// + /// Cycle counts, 256 entries. + /// + private int[] mCycleCounts; + + /// + /// Cycle count modifiers, 256 entries. + /// + private OpDef.CycleMod[] mCycleMods; + + /// + /// List of "interesting" CPUs. These will be presented in the system definition + /// file when starting a new project. The actual set of CPUs that are unique (from + /// our perspective) is much smaller, as most of these can be represented accurately + /// (for disassembly purposes) by a handful of archetypes. + /// + public enum CpuType { + CpuUnknown = 0, + Cpu6502, // Apple ][+ + Cpu6502B, // Atari 800 + Cpu6507, // Atari 2600 + Cpu6502C, // Atari 5200 + Cpu6510, // Commodore 64 + Cpu8502, // Commodore 128 + Cpu2A03, // NES + Cpu65C02, // Apple //e + Cpu65802, // ? + Cpu65816, // Apple IIgs + Cpu5A22 // SNES + } + + /// + /// Converts a CPU type name string to a CpuType value. Used for deserialization. + /// + /// Case-sensitive CPU name + /// CpuType value, or CpuUnknown if name wasn't recognized. + public static CpuType GetCpuTypeFromName(string name) { + switch (name) { + case "6502": return CpuType.Cpu6502; + case "6502B": return CpuType.Cpu6502B; + case "6502C": return CpuType.Cpu6502C; + case "6507": return CpuType.Cpu6507; + case "6510": return CpuType.Cpu6510; + case "8502": return CpuType.Cpu8502; + case "2A03": return CpuType.Cpu2A03; + case "65C02": return CpuType.Cpu65C02; + case "65802": return CpuType.Cpu65802; + case "65816": return CpuType.Cpu65816; + case "5A22": return CpuType.Cpu5A22; + default: + return CpuType.CpuUnknown; + } + } + + /// + /// Converts a CpuType value to a CPU type name string. Used for serialization. + /// + /// CPU type. + /// CPU name string. + public static string GetCpuNameFromType(CpuType type) { + switch (type) { + case CpuType.Cpu6502: return "6502"; + case CpuType.Cpu6502B: return "6502B"; + case CpuType.Cpu6502C: return "6502C"; + case CpuType.Cpu6507: return "6507"; + case CpuType.Cpu6510: return "6510"; + case CpuType.Cpu8502: return "8502"; + case CpuType.Cpu2A03: return "2A03"; + case CpuType.Cpu65C02: return "65C02"; + case CpuType.Cpu65802: return "65802"; + case CpuType.Cpu65816: return "65816"; + case CpuType.Cpu5A22: return "5A22"; + default: + return "65??"; + } + } + + /// + /// Generates a CpuDef that best matches the parameters. + /// + /// Specific CPU we want. + /// Set to true if "undocumented" opcodes should + /// be included in the definition. + /// Best CpuDef. + public static CpuDef GetBestMatch(CpuType type, bool includeUndocumented) { + // Pretty much everything boils down to a 6502, 65C02, or 65816. + // The Rockwell R65C02, described in an appendix in Eyes & Lichty, doesn't, + // and would need its own CpuDef. + CpuDef cpuDef; + switch (type) { + case CpuType.Cpu65802: + case CpuType.Cpu65816: + case CpuType.Cpu5A22: + cpuDef = Cpu65816; + break; + case CpuType.Cpu65C02: + cpuDef = Cpu65C02; + break; + default: + cpuDef = Cpu6502; + break; + } + + cpuDef.GenerateCycleCounts(); + + // If we don't want undocumented opcodes, strip them out of the definition. + // Entries are replaced with OpInvalid. + if (!includeUndocumented) { + CpuDef stripped = new CpuDef(cpuDef); + for (int i = 0; i < 256; i++) { + if (cpuDef.mOpDefs[i].IsUndocumented) { + stripped.mOpDefs[i] = OpDef.OpInvalid; + } else { + stripped.mOpDefs[i] = cpuDef.mOpDefs[i]; + } + } + cpuDef = stripped; + } + + cpuDef.HasUndocumented = includeUndocumented; + + return cpuDef; + } + + /// + /// Constructor. + /// + /// Human-readable name. + /// Address space bits (16 or 24). + private CpuDef(string name, int maxAddressValue, bool hasEmuFlag) { + Name = name; + MaxAddressValue = maxAddressValue; + HasEmuFlag = hasEmuFlag; + HasUndocumented = true; + } + + /// + /// Copy constructor. Does not copy the contents of mOpDefs. Only used internally. + /// + /// Object to copy from. + private CpuDef(CpuDef src) { + Name = src.Name; + MaxAddressValue = src.MaxAddressValue; + HasEmuFlag = src.HasEmuFlag; + Type = src.Type; + mCycleCounts = src.mCycleCounts; + mCycleMods = src.mCycleMods; + + mOpDefs = new OpDef[256]; + } + + + /// + /// Returns an entry from the OpDef array for the specified opcode, 0-255. (We could + /// probably just make this the class indexer.) + /// + /// Instruction opcode + /// Instruction definition. + public OpDef GetOpDef(int op) { return mOpDefs[op]; } + + /// + /// Returns the number of cycles required to execute the instruction. If the value + /// is negative, the negated value represents the minimum number of cycles for an + /// instruction with variable timing. + /// + /// The value returned will factor in any CPU-specific aspects. + /// + /// Instruction opcode value. + /// Cycle count. + public int GetCycles(int opNum, StatusFlags flags, OpDef.BranchTaken branchTaken, + bool branchCrossesPage) { + // The irrelevant modifiers have already been stripped out. + OpDef.CycleMod mods = mCycleMods[opNum]; + int cycles = mCycleCounts[opNum]; + + // Walk through the various cycle mods. If we can evaluate them definitively, + // do so now and remove them from the set. + + // The M/X flags are defined to be in one state or the other, even when the flag + // value is indeterminate, because we have to be able to size immediate operands + // appropriately. So there's no ambiguity here even when there's ambiguity. We + // make a similar statement about the E flag. + if ((mods & OpDef.CycleMod.OneIfM0) != 0) { + if (!flags.ShortM) { + cycles++; + } + mods &= ~OpDef.CycleMod.OneIfM0; + } + if ((mods & OpDef.CycleMod.TwoIfM0) != 0) { + if (!flags.ShortM) { + cycles += 2; + } + mods &= ~OpDef.CycleMod.TwoIfM0; + } + if ((mods & OpDef.CycleMod.OneIfX0) != 0) { + if (!flags.ShortX) { + cycles++; + } + mods &= ~OpDef.CycleMod.OneIfX0; + } + if ((mods & OpDef.CycleMod.OneIfE0) != 0) { + if (flags.E == 0) { + cycles++; + } + mods &= ~OpDef.CycleMod.OneIfE0; + } + + // Some of these can be known, some can't. + if ((mods & OpDef.CycleMod.OneIfD1) != 0) { + if (flags.D == 1) { + cycles++; + } + if (flags.D == 0 || flags.D == 1) { + mods &= ~OpDef.CycleMod.OneIfD1; + } + } + if ((mods & OpDef.CycleMod.OneIfBranchTaken) != 0) { + if (branchTaken == OpDef.BranchTaken.Always) { + cycles++; + } + if (branchTaken != OpDef.BranchTaken.Indeterminate) { + mods &= ~OpDef.CycleMod.OneIfBranchTaken; + } + } + if ((mods & OpDef.CycleMod.OneIfBranchPage) != 0) { + if (branchCrossesPage && flags.E != 0) { + cycles++; // +1 unless we're in native mode on 65816 + } + mods &= ~OpDef.CycleMod.OneIfBranchPage; + } + + // We can't evaluate OneIfDpNonzero, OneIfIndexPage, or MinusOneIfNoPage. + // OneIf65C02 was handled earlier. + // TODO(maybe): in some cases we can know that the index doesn't cross a + // page boundary by checking the address, e.g. "LDA $2000,X" can't cross. + + if (mods != 0) { + // Some unresolved mods remain. + cycles = -cycles; + } + return cycles; + } + + + /// + /// Consistency test. + /// + /// True on success. + public static bool DebugValidate() { + InternalValidate(Cpu6502); + InternalValidate(Cpu65C02); + InternalValidate(Cpu65816); + Debug.WriteLine("CpuDefs okay"); + return true; + } + private static void InternalValidate(CpuDef cdef) { + for (int i = 0; i < 256; i++) { + OpDef op = cdef.mOpDefs[i]; + if (op.Opcode != i && op.AddrMode != OpDef.AddressMode.Unknown) { + throw new Exception("CpuDef for " + cdef.Type + ": entry 0x" + + i.ToString("x") + " has value " + op.Opcode.ToString("x")); + } + if (op.AddrMode != OpDef.AddressMode.Unknown && op.Cycles == 0) { + throw new Exception("Instruction 0x" + i.ToString("x2") + ": " + + op + " missing cycles"); + } + } + } + + public override string ToString() { + return Name + " (has16=" + HasAddr16 + ", hasEmu=" + HasEmuFlag + ")"; + } + + /// + /// Generates the mCycleCounts and mCycleMods arrays. + /// + private void GenerateCycleCounts() { + if (mCycleCounts != null) { + return; + } + mCycleCounts = new int[256]; + mCycleMods = new OpDef.CycleMod[256]; + + // Figure out which mods apply for this CPU. + OpDef.CycleMod ignoreMask = 0; + switch (Type) { + case CpuType.Cpu6502: + ignoreMask = OpDef.CycleMod.OneIfM0 | + OpDef.CycleMod.TwoIfM0 | + OpDef.CycleMod.OneIfX0 | + OpDef.CycleMod.OneIfDpNonzero | + OpDef.CycleMod.OneIfD1 | + OpDef.CycleMod.OneIfE0 | + OpDef.CycleMod.OneIf65C02 | + OpDef.CycleMod.MinusOneIfNoPage | + OpDef.CycleMod.BlockMove; + break; + case CpuType.Cpu65C02: + ignoreMask = OpDef.CycleMod.OneIfM0 | + OpDef.CycleMod.TwoIfM0 | + OpDef.CycleMod.OneIfX0 | + OpDef.CycleMod.OneIfDpNonzero | + OpDef.CycleMod.OneIfE0 | + OpDef.CycleMod.BlockMove; + break; + case CpuType.Cpu65816: + ignoreMask = OpDef.CycleMod.OneIfD1 | + OpDef.CycleMod.OneIf65C02 | + OpDef.CycleMod.MinusOneIfNoPage; + break; + default: + Debug.Assert(false, "unsupported cpu type " + Type); + return; + } + + // If an instruction has one or more applicable mods, declare it as variable. + for (int i = 0; i < 256; i++) { + OpDef op = mOpDefs[i]; + int baseCycles = op.Cycles; + OpDef.CycleMod mods = op.CycleMods & ~ignoreMask; + if ((mods & OpDef.CycleMod.OneIf65C02) != 0) { + // This isn't variable -- the instruction always takes one cycle longer + // on the 65C02. (Applies to $6C, JMP (addr).) + Debug.Assert(Type == CpuType.Cpu65C02); + baseCycles++; + mods &= ~OpDef.CycleMod.OneIf65C02; + } + mCycleCounts[i] = baseCycles; + mCycleMods[i] = mods; + } + } + + + // Classic MOS 6502, with full set of undocumented opcodes. + private static CpuDef Cpu6502 { get; } = new CpuDef("MOS 6502", (1 << 16) - 1, false) { + Type = CpuType.Cpu6502, + mOpDefs = new OpDef[] { + OpDef.OpBRK_StackInt, // 0x00 + OpDef.OpORA_DPIndexXInd, + OpDef.GenerateUndoc(0x02, OpDef.OpHLT_Implied), + OpDef.OpSLO_DPIndexXInd, + OpDef.GenerateUndoc(0x04, OpDef.OpDOP_DP), + OpDef.OpORA_DP, + OpDef.OpASL_DP, + OpDef.OpSLO_DP, + OpDef.OpPHP_StackPush, // 0x08 + OpDef.OpORA_Imm, + OpDef.OpASL_Acc, + OpDef.GenerateUndoc(0x0b, OpDef.OpANC_Imm), + OpDef.OpTOP_Abs, + OpDef.OpORA_Abs, + OpDef.OpASL_Abs, + OpDef.OpSLO_Absolute, + OpDef.OpBPL_PCRel, // 0x10 + OpDef.OpORA_DPIndIndexY, + OpDef.GenerateUndoc(0x12, OpDef.OpHLT_Implied), + OpDef.OpSLO_DPIndIndexY, + OpDef.GenerateUndoc(0x14, OpDef.OpDOP_DPIndexX), + OpDef.OpORA_DPIndexX, + OpDef.OpASL_DPIndexX, + OpDef.OpSLO_DPIndexX, + OpDef.OpCLC_Implied, // 0x18 + OpDef.OpORA_AbsIndexY, + OpDef.GenerateUndoc(0x1a, OpDef.OpNOP_Implied), + OpDef.OpSLO_AbsIndexY, + OpDef.GenerateUndoc(0x1c, OpDef.OpTOP_AbsIndeX), + OpDef.OpORA_AbsIndexX, + OpDef.OpASL_AbsIndexX, + OpDef.OpSLO_AbsIndexX, + OpDef.OpJSR_Abs, // 0x20 + OpDef.OpAND_DPIndexXInd, + OpDef.GenerateUndoc(0x22, OpDef.OpHLT_Implied), + OpDef.OpRLA_DPIndexXInd, + OpDef.OpBIT_DP, + OpDef.OpAND_DP, + OpDef.OpROL_DP, + OpDef.OpRLA_DP, + OpDef.OpPLP_StackPull, // 0x28 + OpDef.OpAND_Imm, + OpDef.OpROL_Acc, + OpDef.GenerateUndoc(0x2b, OpDef.OpANC_Imm), + OpDef.OpBIT_Abs, + OpDef.OpAND_Abs, + OpDef.OpROL_Abs, + OpDef.OpRLA_Absolute, + OpDef.OpBMI_PCRel, // 0x30 + OpDef.OpAND_DPIndIndexY, + OpDef.GenerateUndoc(0x32, OpDef.OpHLT_Implied), + OpDef.OpRLA_DPIndIndexY, + OpDef.GenerateUndoc(0x34, OpDef.OpDOP_DPIndexX), + OpDef.OpAND_DPIndexX, + OpDef.OpROL_DPIndexX, + OpDef.OpRLA_DPIndexX, + OpDef.OpSEC_Implied, // 0x38 + OpDef.OpAND_AbsIndexY, + OpDef.GenerateUndoc(0x3a, OpDef.OpNOP_Implied), + OpDef.OpRLA_AbsIndexY, + OpDef.GenerateUndoc(0x3c, OpDef.OpTOP_AbsIndeX), + OpDef.OpAND_AbsIndexX, + OpDef.OpROL_AbsIndexX, + OpDef.OpRLA_AbsIndexX, + OpDef.OpRTI_StackRTI, // 0x40 + OpDef.OpEOR_DPIndexXInd, + OpDef.GenerateUndoc(0x42, OpDef.OpHLT_Implied), + OpDef.OpSRE_DPIndexXInd, + OpDef.GenerateUndoc(0x44, OpDef.OpDOP_DP), + OpDef.OpEOR_DP, + OpDef.OpLSR_DP, + OpDef.OpSRE_DP, + OpDef.OpPHA_StackPush, // 0x48 + OpDef.OpEOR_Imm, + OpDef.OpLSR_Acc, + OpDef.OpASR_Imm, + OpDef.OpJMP_Abs, + OpDef.OpEOR_Abs, + OpDef.OpLSR_Abs, + OpDef.OpSRE_Absolute, + OpDef.OpBVC_PCRel, // 0x50 + OpDef.OpEOR_DPIndIndexY, + OpDef.GenerateUndoc(0x52, OpDef.OpHLT_Implied), + OpDef.OpSRE_DPIndIndexY, + OpDef.GenerateUndoc(0x54, OpDef.OpDOP_DPIndexX), + OpDef.OpEOR_DPIndexX, + OpDef.OpLSR_DPIndexX, + OpDef.OpSRE_DPIndexX, + OpDef.OpCLI_Implied, // 0x58 + OpDef.OpEOR_AbsIndexY, + OpDef.GenerateUndoc(0x5a, OpDef.OpNOP_Implied), + OpDef.OpSRE_AbsIndexY, + OpDef.GenerateUndoc(0x5c, OpDef.OpTOP_AbsIndeX), + OpDef.OpEOR_AbsIndexX, + OpDef.OpLSR_AbsIndexX, + OpDef.OpSRE_AbsIndexX, + OpDef.OpRTS_StackRTS, // 0x60 + OpDef.OpADC_DPIndexXInd, + OpDef.GenerateUndoc(0x62, OpDef.OpHLT_Implied), + OpDef.OpRRA_DPIndexXInd, + OpDef.GenerateUndoc(0x64, OpDef.OpDOP_DP), + OpDef.OpADC_DP, + OpDef.OpROR_DP, + OpDef.OpRRA_DP, + OpDef.OpPLA_StackPull, // 0x68 + OpDef.OpADC_Imm, + OpDef.OpROR_Acc, + OpDef.OpARR_Imm, + OpDef.OpJMP_AbsInd, + OpDef.OpADC_Abs, + OpDef.OpROR_Abs, + OpDef.OpRRA_Absolute, + OpDef.OpBVS_PCRel, // 0x70 + OpDef.OpADC_DPIndIndexY, + OpDef.GenerateUndoc(0x72, OpDef.OpHLT_Implied), + OpDef.OpRRA_DPIndIndexY, + OpDef.GenerateUndoc(0x74, OpDef.OpDOP_DPIndexX), + OpDef.OpADC_DPIndexX, + OpDef.OpROR_DPIndexX, + OpDef.OpRRA_DPIndexX, + OpDef.OpSEI_Implied, // 0x78 + OpDef.OpADC_AbsIndexY, + OpDef.GenerateUndoc(0x7a, OpDef.OpNOP_Implied), + OpDef.OpRRA_AbsIndexY, + OpDef.GenerateUndoc(0x7c, OpDef.OpTOP_AbsIndeX), + OpDef.OpADC_AbsIndexX, + OpDef.OpROR_AbsIndexX, + OpDef.OpRRA_AbsIndexX, + OpDef.GenerateUndoc(0x80, OpDef.OpDOP_Imm), // 0x80 + OpDef.OpSTA_DPIndexXInd, + OpDef.GenerateUndoc(0x82, OpDef.OpDOP_Imm), + OpDef.OpSAX_DPIndexXInd, + OpDef.OpSTY_DP, + OpDef.OpSTA_DP, + OpDef.OpSTX_DP, + OpDef.OpSAX_DP, + OpDef.OpDEY_Implied, // 0x88 + OpDef.GenerateUndoc(0x89, OpDef.OpDOP_Imm), + OpDef.OpTXA_Implied, + OpDef.OpANE_Imm, + OpDef.OpSTY_Abs, + OpDef.OpSTA_Abs, + OpDef.OpSTX_Abs, + OpDef.OpSAX_Absolute, + OpDef.OpBCC_PCRel, // 0x90 + OpDef.OpSTA_DPIndIndexY, + OpDef.GenerateUndoc(0x92, OpDef.OpHLT_Implied), + OpDef.OpSHA_DPIndIndexY, + OpDef.OpSTY_DPIndexX, + OpDef.OpSTA_DPIndexX, + OpDef.OpSTX_DPIndexY, + OpDef.OpSAX_DPIndexY, + OpDef.OpTYA_Implied, // 0x98 + OpDef.OpSTA_AbsIndexY, + OpDef.OpTXS_Implied, + OpDef.OpSHS_AbsIndexY, + OpDef.OpSHY_AbsIndexX, + OpDef.OpSTA_AbsIndexX, + OpDef.OpSHX_AbsIndexY, + OpDef.OpSHA_AbsIndexY, + OpDef.OpLDY_Imm, // 0xa0 + OpDef.OpLDA_DPIndexXInd, + OpDef.OpLDX_Imm, + OpDef.OpLAX_DPIndexXInd, + OpDef.OpLDY_DP, + OpDef.OpLDA_DP, + OpDef.OpLDX_DP, + OpDef.OpLAX_DP, + OpDef.OpTAY_Implied, // 0xa8 + OpDef.OpLDA_Imm, + OpDef.OpTAX_Implied, + OpDef.OpLXA_Imm, + OpDef.OpLDY_Abs, + OpDef.OpLDA_Abs, + OpDef.OpLDX_Abs, + OpDef.OpLAX_Absolute, + OpDef.OpBCS_PCRel, // 0xb0 + OpDef.OpLDA_DPIndIndexY, + OpDef.GenerateUndoc(0xb2, OpDef.OpHLT_Implied), + OpDef.OpLAX_DPIndIndexY, + OpDef.OpLDY_DPIndexX, + OpDef.OpLDA_DPIndexX, + OpDef.OpLDX_DPIndexY, + OpDef.OpLAX_DPIndexY, + OpDef.OpCLV_Implied, // 0xb8 + OpDef.OpLDA_AbsIndexY, + OpDef.OpTSX_Implied, + OpDef.OpLAE_AbsIndexY, + OpDef.OpLDY_AbsIndexX, + OpDef.OpLDA_AbsIndexX, + OpDef.OpLDX_AbsIndexY, + OpDef.OpLAX_AbsIndexY, + OpDef.OpCPY_Imm, // 0xc0 + OpDef.OpCMP_DPIndexXInd, + OpDef.GenerateUndoc(0xc2, OpDef.OpDOP_Imm), + OpDef.OpDCP_DPIndexXInd, + OpDef.OpCPY_DP, + OpDef.OpCMP_DP, + OpDef.OpDEC_DP, + OpDef.OpDCP_DP, + OpDef.OpINY_Implied, // 0xc8 + OpDef.OpCMP_Imm, + OpDef.OpDEX_Implied, + OpDef.OpSBX_Imm, + OpDef.OpCPY_Abs, + OpDef.OpCMP_Abs, + OpDef.OpDEC_Abs, + OpDef.OpDCP_Abs, + OpDef.OpBNE_PCRel, // 0xd0 + OpDef.OpCMP_DPIndIndexY, + OpDef.GenerateUndoc(0xd2, OpDef.OpHLT_Implied), + OpDef.OpDCP_DPIndIndexY, + OpDef.GenerateUndoc(0xd4, OpDef.OpDOP_DPIndexX), + OpDef.OpCMP_DPIndexX, + OpDef.OpDEC_DPIndexX, + OpDef.OpDCP_DPIndexX, + OpDef.OpCLD_Implied, // 0xd8 + OpDef.OpCMP_AbsIndexY, + OpDef.GenerateUndoc(0xda, OpDef.OpNOP_Implied), + OpDef.OpDCP_AbsIndexY, + OpDef.GenerateUndoc(0xdc, OpDef.OpTOP_AbsIndeX), + OpDef.OpCMP_AbsIndexX, + OpDef.OpDEC_AbsIndexX, + OpDef.OpDCP_AbsIndexX, + OpDef.OpCPX_Imm, // 0xe0 + OpDef.OpSBC_DPIndexXInd, + OpDef.GenerateUndoc(0xe2, OpDef.OpDOP_Imm), + OpDef.OpISB_DPIndexXInd, + OpDef.OpCPX_DP, + OpDef.OpSBC_DP, + OpDef.OpINC_DP, + OpDef.OpISB_DP, + OpDef.OpINX_Implied, // 0xe8 + OpDef.OpSBC_Imm, + OpDef.OpNOP_Implied, + OpDef.GenerateUndoc(0xeb, OpDef.OpSBC_Imm), + OpDef.OpCPX_Abs, + OpDef.OpSBC_Abs, + OpDef.OpINC_Abs, + OpDef.OpISB_Abs, + OpDef.OpBEQ_PCRel, // 0xf0 + OpDef.OpSBC_DPIndIndexY, + OpDef.GenerateUndoc(0xf2, OpDef.OpHLT_Implied), + OpDef.OpISB_DPIndIndexY, + OpDef.GenerateUndoc(0xf4, OpDef.OpDOP_DPIndexX), + OpDef.OpSBC_DPIndexX, + OpDef.OpINC_DPIndexX, + OpDef.OpISB_DPIndexX, + OpDef.OpSED_Implied, // 0xf8 + OpDef.OpSBC_AbsIndexY, + OpDef.GenerateUndoc(0xfa, OpDef.OpNOP_Implied), + OpDef.OpISB_AbsIndexY, + OpDef.GenerateUndoc(0xfc, OpDef.OpTOP_AbsIndeX), + OpDef.OpSBC_AbsIndexX, + OpDef.OpINC_AbsIndexX, + OpDef.OpISB_AbsIndexX, + } + }; + + + // WDC's 65C02, with new opcodes and a handful of slightly strange NOPs. + private static CpuDef Cpu65C02 { get; } = new CpuDef("WDC W65C02S", (1 << 16) - 1, false) { + Type = CpuType.Cpu65C02, + mOpDefs = new OpDef[] { + OpDef.OpBRK_StackInt, // 0x00 + OpDef.OpORA_DPIndexXInd, + OpDef.GenerateUndoc(0x02, OpDef.OpLDD_Imm), + OpDef.GenerateUndoc(0x03, OpDef.OpNOP_65C02), + OpDef.OpTSB_DP, + OpDef.OpORA_DP, + OpDef.OpASL_DP, + OpDef.GenerateUndoc(0x07, OpDef.OpNOP_65C02), + OpDef.OpPHP_StackPush, // 0x08 + OpDef.OpORA_Imm, + OpDef.OpASL_Acc, + OpDef.GenerateUndoc(0x0b, OpDef.OpNOP_65C02), + OpDef.OpTSB_Abs, + OpDef.OpORA_Abs, + OpDef.OpASL_Abs, + OpDef.GenerateUndoc(0x0f, OpDef.OpNOP_65C02), + OpDef.OpBPL_PCRel, // 0x10 + OpDef.OpORA_DPIndIndexY, + OpDef.OpORA_DPInd, + OpDef.GenerateUndoc(0x13, OpDef.OpNOP_65C02), + OpDef.OpTRB_DP, + OpDef.OpORA_DPIndexX, + OpDef.OpASL_DPIndexX, + OpDef.GenerateUndoc(0x17, OpDef.OpNOP_65C02), + OpDef.OpCLC_Implied, // 0x18 + OpDef.OpORA_AbsIndexY, + OpDef.OpINC_Acc, + OpDef.GenerateUndoc(0x1b, OpDef.OpNOP_65C02), + OpDef.OpTRB_Abs, + OpDef.OpORA_AbsIndexX, + OpDef.OpASL_AbsIndexX, + OpDef.GenerateUndoc(0x1f, OpDef.OpNOP_65C02), + OpDef.OpJSR_Abs, // 0x20 + OpDef.OpAND_DPIndexXInd, + OpDef.GenerateUndoc(0x22, OpDef.OpLDD_Imm), + OpDef.GenerateUndoc(0x23, OpDef.OpNOP_65C02), + OpDef.OpBIT_DP, + OpDef.OpAND_DP, + OpDef.OpROL_DP, + OpDef.GenerateUndoc(0x27, OpDef.OpNOP_65C02), + OpDef.OpPLP_StackPull, // 0x28 + OpDef.OpAND_Imm, + OpDef.OpROL_Acc, + OpDef.GenerateUndoc(0x2b, OpDef.OpNOP_65C02), + OpDef.OpBIT_Abs, + OpDef.OpAND_Abs, + OpDef.OpROL_Abs, + OpDef.GenerateUndoc(0x2f, OpDef.OpNOP_65C02), + OpDef.OpBMI_PCRel, // 0x30 + OpDef.OpAND_DPIndIndexY, + OpDef.OpAND_DPInd, + OpDef.GenerateUndoc(0x33, OpDef.OpNOP_65C02), + OpDef.OpBIT_DPIndexX, + OpDef.OpAND_DPIndexX, + OpDef.OpROL_DPIndexX, + OpDef.GenerateUndoc(0x37, OpDef.OpNOP_65C02), + OpDef.OpSEC_Implied, // 0x38 + OpDef.OpAND_AbsIndexY, + OpDef.OpDEC_Acc, + OpDef.GenerateUndoc(0x3b, OpDef.OpNOP_65C02), + OpDef.OpBIT_AbsIndexX, + OpDef.OpAND_AbsIndexX, + OpDef.OpROL_AbsIndexX, + OpDef.GenerateUndoc(0x3f, OpDef.OpNOP_65C02), + OpDef.OpRTI_StackRTI, // 0x40 + OpDef.OpEOR_DPIndexXInd, + OpDef.GenerateUndoc(0x42, OpDef.OpLDD_Imm), + OpDef.GenerateUndoc(0x43, OpDef.OpNOP_65C02), + OpDef.GenerateUndoc(0x44, OpDef.OpLDD_DP), + OpDef.OpEOR_DP, + OpDef.OpLSR_DP, + OpDef.GenerateUndoc(0x47, OpDef.OpNOP_65C02), + OpDef.OpPHA_StackPush, // 0x48 + OpDef.OpEOR_Imm, + OpDef.OpLSR_Acc, + OpDef.GenerateUndoc(0x4b, OpDef.OpNOP_65C02), + OpDef.OpJMP_Abs, + OpDef.OpEOR_Abs, + OpDef.OpLSR_Abs, + OpDef.GenerateUndoc(0x4f, OpDef.OpNOP_65C02), + OpDef.OpBVC_PCRel, // 0x50 + OpDef.OpEOR_DPIndIndexY, + OpDef.OpEOR_DPInd, + OpDef.GenerateUndoc(0x53, OpDef.OpNOP_65C02), + OpDef.GenerateUndoc(0x54, OpDef.OpLDD_DPIndexX), + OpDef.OpEOR_DPIndexX, + OpDef.OpLSR_DPIndexX, + OpDef.GenerateUndoc(0x57, OpDef.OpNOP_65C02), + OpDef.OpCLI_Implied, // 0x58 + OpDef.OpEOR_AbsIndexY, + OpDef.OpPHY_StackPush, + OpDef.GenerateUndoc(0x5b, OpDef.OpNOP_65C02), + OpDef.GenerateUndoc(0x5c, OpDef.OpLDD_Weird), + OpDef.OpEOR_AbsIndexX, + OpDef.OpLSR_AbsIndexX, + OpDef.GenerateUndoc(0x5f, OpDef.OpNOP_65C02), + OpDef.OpRTS_StackRTS, // 0x60 + OpDef.OpADC_DPIndexXInd, + OpDef.GenerateUndoc(0x62, OpDef.OpLDD_Imm), + OpDef.GenerateUndoc(0x63, OpDef.OpNOP_65C02), + OpDef.OpSTZ_DP, + OpDef.OpADC_DP, + OpDef.OpROR_DP, + OpDef.GenerateUndoc(0x67, OpDef.OpNOP_65C02), + OpDef.OpPLA_StackPull, // 0x68 + OpDef.OpADC_Imm, + OpDef.OpROR_Acc, + OpDef.GenerateUndoc(0x6b, OpDef.OpNOP_65C02), + OpDef.OpJMP_AbsInd, + OpDef.OpADC_Abs, + OpDef.OpROR_Abs, + OpDef.GenerateUndoc(0x6f, OpDef.OpNOP_65C02), + OpDef.OpBVS_PCRel, // 0x70 + OpDef.OpADC_DPIndIndexY, + OpDef.OpADC_DPInd, + OpDef.GenerateUndoc(0x73, OpDef.OpNOP_65C02), + OpDef.OpSTZ_DPIndexX, + OpDef.OpADC_DPIndexX, + OpDef.OpROR_DPIndexX, + OpDef.GenerateUndoc(0x77, OpDef.OpNOP_65C02), + OpDef.OpSEI_Implied, // 0x78 + OpDef.OpADC_AbsIndexY, + OpDef.OpPLY_StackPull, + OpDef.GenerateUndoc(0x7b, OpDef.OpNOP_65C02), + OpDef.OpJMP_AbsIndexXInd, + OpDef.OpADC_AbsIndexX, + OpDef.OpROR_AbsIndexX, + OpDef.GenerateUndoc(0x7f, OpDef.OpNOP_65C02), + OpDef.OpBRA_PCRel, // 0x80 + OpDef.OpSTA_DPIndexXInd, + OpDef.GenerateUndoc(0x82, OpDef.OpLDD_Imm), + OpDef.GenerateUndoc(0x83, OpDef.OpNOP_65C02), + OpDef.OpSTY_DP, + OpDef.OpSTA_DP, + OpDef.OpSTX_DP, + OpDef.GenerateUndoc(0x87, OpDef.OpNOP_65C02), + OpDef.OpDEY_Implied, // 0x88 + OpDef.OpBIT_Imm, + OpDef.OpTXA_Implied, + OpDef.GenerateUndoc(0x8b, OpDef.OpNOP_65C02), + OpDef.OpSTY_Abs, + OpDef.OpSTA_Abs, + OpDef.OpSTX_Abs, + OpDef.GenerateUndoc(0x8f, OpDef.OpNOP_65C02), + OpDef.OpBCC_PCRel, // 0x90 + OpDef.OpSTA_DPIndIndexY, + OpDef.OpSTA_DPInd, + OpDef.GenerateUndoc(0x93, OpDef.OpNOP_65C02), + OpDef.OpSTY_DPIndexX, + OpDef.OpSTA_DPIndexX, + OpDef.OpSTX_DPIndexY, + OpDef.GenerateUndoc(0x97, OpDef.OpNOP_65C02), + OpDef.OpTYA_Implied, // 0x98 + OpDef.OpSTA_AbsIndexY, + OpDef.OpTXS_Implied, + OpDef.GenerateUndoc(0x9b, OpDef.OpNOP_65C02), + OpDef.OpSTZ_Abs, + OpDef.OpSTA_AbsIndexX, + OpDef.OpSTZ_AbsIndexX, + OpDef.GenerateUndoc(0x9f, OpDef.OpNOP_65C02), + OpDef.OpLDY_Imm, // 0xa0 + OpDef.OpLDA_DPIndexXInd, + OpDef.OpLDX_Imm, + OpDef.GenerateUndoc(0xa3, OpDef.OpNOP_65C02), + OpDef.OpLDY_DP, + OpDef.OpLDA_DP, + OpDef.OpLDX_DP, + OpDef.GenerateUndoc(0xa7, OpDef.OpNOP_65C02), + OpDef.OpTAY_Implied, // 0xa8 + OpDef.OpLDA_Imm, + OpDef.OpTAX_Implied, + OpDef.GenerateUndoc(0xab, OpDef.OpNOP_65C02), + OpDef.OpLDY_Abs, + OpDef.OpLDA_Abs, + OpDef.OpLDX_Abs, + OpDef.GenerateUndoc(0xaf, OpDef.OpNOP_65C02), + OpDef.OpBCS_PCRel, // 0xb0 + OpDef.OpLDA_DPIndIndexY, + OpDef.OpLDA_DPInd, + OpDef.GenerateUndoc(0xb3, OpDef.OpNOP_65C02), + OpDef.OpLDY_DPIndexX, + OpDef.OpLDA_DPIndexX, + OpDef.OpLDX_DPIndexY, + OpDef.GenerateUndoc(0xb7, OpDef.OpNOP_65C02), + OpDef.OpCLV_Implied, // 0xb8 + OpDef.OpLDA_AbsIndexY, + OpDef.OpTSX_Implied, + OpDef.GenerateUndoc(0xbb, OpDef.OpNOP_65C02), + OpDef.OpLDY_AbsIndexX, + OpDef.OpLDA_AbsIndexX, + OpDef.OpLDX_AbsIndexY, + OpDef.GenerateUndoc(0xbf, OpDef.OpNOP_65C02), + OpDef.OpCPY_Imm, // 0xc0 + OpDef.OpCMP_DPIndexXInd, + OpDef.GenerateUndoc(0xc2, OpDef.OpLDD_Imm), + OpDef.GenerateUndoc(0xc3, OpDef.OpNOP_65C02), + OpDef.OpCPY_DP, + OpDef.OpCMP_DP, + OpDef.OpDEC_DP, + OpDef.GenerateUndoc(0xc7, OpDef.OpNOP_65C02), + OpDef.OpINY_Implied, // 0xc8 + OpDef.OpCMP_Imm, + OpDef.OpDEX_Implied, + OpDef.GenerateUndoc(0xcb, OpDef.OpNOP_65C02), + OpDef.OpCPY_Abs, + OpDef.OpCMP_Abs, + OpDef.OpDEC_Abs, + OpDef.GenerateUndoc(0xcf, OpDef.OpNOP_65C02), + OpDef.OpBNE_PCRel, // 0xd0 + OpDef.OpCMP_DPIndIndexY, + OpDef.OpCMP_DPInd, + OpDef.GenerateUndoc(0xd3, OpDef.OpNOP_65C02), + OpDef.GenerateUndoc(0xd4, OpDef.OpLDD_DPIndexX), + OpDef.OpCMP_DPIndexX, + OpDef.OpDEC_DPIndexX, + OpDef.GenerateUndoc(0xd7, OpDef.OpNOP_65C02), + OpDef.OpCLD_Implied, // 0xd8 + OpDef.OpCMP_AbsIndexY, + OpDef.OpPHX_StackPush, + OpDef.GenerateUndoc(0xdb, OpDef.OpNOP_65C02), + OpDef.GenerateUndoc(0xdc, OpDef.OpLDD_Absolute), + OpDef.OpCMP_AbsIndexX, + OpDef.OpDEC_AbsIndexX, + OpDef.GenerateUndoc(0xdf, OpDef.OpNOP_65C02), + OpDef.OpCPX_Imm, // 0xe0 + OpDef.OpSBC_DPIndexXInd, + OpDef.GenerateUndoc(0xe2, OpDef.OpLDD_Imm), + OpDef.GenerateUndoc(0xe3, OpDef.OpNOP_65C02), + OpDef.OpCPX_DP, + OpDef.OpSBC_DP, + OpDef.OpINC_DP, + OpDef.GenerateUndoc(0xe7, OpDef.OpNOP_65C02), + OpDef.OpINX_Implied, // 0xe8 + OpDef.OpSBC_Imm, + OpDef.OpNOP_Implied, + OpDef.GenerateUndoc(0xeb, OpDef.OpNOP_65C02), + OpDef.OpCPX_Abs, + OpDef.OpSBC_Abs, + OpDef.OpINC_Abs, + OpDef.GenerateUndoc(0xef, OpDef.OpNOP_65C02), + OpDef.OpBEQ_PCRel, // 0xf0 + OpDef.OpSBC_DPIndIndexY, + OpDef.OpSBC_DPInd, + OpDef.GenerateUndoc(0xf3, OpDef.OpNOP_65C02), + OpDef.GenerateUndoc(0xf4, OpDef.OpLDD_DPIndexX), + OpDef.OpSBC_DPIndexX, + OpDef.OpINC_DPIndexX, + OpDef.GenerateUndoc(0xf7, OpDef.OpNOP_65C02), + OpDef.OpSED_Implied, // 0xf8 + OpDef.OpSBC_AbsIndexY, + OpDef.OpPLX_StackPull, + OpDef.GenerateUndoc(0xfb, OpDef.OpNOP_65C02), + OpDef.GenerateUndoc(0xfc, OpDef.OpLDD_Absolute), + OpDef.OpSBC_AbsIndexX, + OpDef.OpINC_AbsIndexX, + OpDef.GenerateUndoc(0xff, OpDef.OpNOP_65C02), + } + }; + + + // WDC 65802 and 65816. No undocumented opcodes -- all 256 are used. + private static CpuDef Cpu65816 { get; } = new CpuDef("WDC W65C816S", (1 << 24) - 1, true) { + Type = CpuType.Cpu65816, + mOpDefs = new OpDef[] { + OpDef.OpBRK_StackInt, // 0x00 + OpDef.OpORA_DPIndexXInd, + OpDef.OpCOP_StackInt, + OpDef.OpORA_StackRel, + OpDef.OpTSB_DP, + OpDef.OpORA_DP, + OpDef.OpASL_DP, + OpDef.OpORA_DPIndLong, + OpDef.OpPHP_StackPush, // 0x08 + OpDef.OpORA_ImmLongA, + OpDef.OpASL_Acc, + OpDef.OpPHD_StackPush, + OpDef.OpTSB_Abs, + OpDef.OpORA_Abs, + OpDef.OpASL_Abs, + OpDef.OpORA_AbsLong, + OpDef.OpBPL_PCRel, // 0x10 + OpDef.OpORA_DPIndIndexY, + OpDef.OpORA_DPInd, + OpDef.OpORA_StackRelIndIndexY, + OpDef.OpTRB_DP, + OpDef.OpORA_DPIndexX, + OpDef.OpASL_DPIndexX, + OpDef.OpORA_DPIndIndexYLong, + OpDef.OpCLC_Implied, // 0x18 + OpDef.OpORA_AbsIndexY, + OpDef.OpINC_Acc, + OpDef.OpTCS_Implied, + OpDef.OpTRB_Abs, + OpDef.OpORA_AbsIndexX, + OpDef.OpASL_AbsIndexX, + OpDef.OpORA_AbsIndexXLong, + OpDef.OpJSR_Abs, // 0x20 + OpDef.OpAND_DPIndexXInd, + OpDef.OpJSR_AbsLong, + OpDef.OpAND_StackRel, + OpDef.OpBIT_DP, + OpDef.OpAND_DP, + OpDef.OpROL_DP, + OpDef.OpAND_DPIndLong, + OpDef.OpPLP_StackPull, // 0x28 + OpDef.OpAND_ImmLongA, + OpDef.OpROL_Acc, + OpDef.OpPLD_StackPull, + OpDef.OpBIT_Abs, + OpDef.OpAND_Abs, + OpDef.OpROL_Abs, + OpDef.OpAND_AbsLong, + OpDef.OpBMI_PCRel, // 0x30 + OpDef.OpAND_DPIndIndexY, + OpDef.OpAND_DPInd, + OpDef.OpAND_StackRelIndIndexY, + OpDef.OpBIT_DPIndexX, + OpDef.OpAND_DPIndexX, + OpDef.OpROL_DPIndexX, + OpDef.OpAND_DPIndIndexYLong, + OpDef.OpSEC_Implied, // 0x38 + OpDef.OpAND_AbsIndexY, + OpDef.OpDEC_Acc, + OpDef.OpTSC_Implied, + OpDef.OpBIT_AbsIndexX, + OpDef.OpAND_AbsIndexX, + OpDef.OpROL_AbsIndexX, + OpDef.OpAND_AbsIndexXLong, + OpDef.OpRTI_StackRTI, // 0x40 + OpDef.OpEOR_DPIndexXInd, + OpDef.OpWDM_WDM, + OpDef.OpEOR_StackRel, + OpDef.OpMVP_BlockMove, + OpDef.OpEOR_DP, + OpDef.OpLSR_DP, + OpDef.OpEOR_DPIndLong, + OpDef.OpPHA_StackPush, // 0x48 + OpDef.OpEOR_ImmLongA, + OpDef.OpLSR_Acc, + OpDef.OpPHK_StackPush, + OpDef.OpJMP_Abs, + OpDef.OpEOR_Abs, + OpDef.OpLSR_Abs, + OpDef.OpEOR_AbsLong, + OpDef.OpBVC_PCRel, // 0x50 + OpDef.OpEOR_DPIndIndexY, + OpDef.OpEOR_DPInd, + OpDef.OpEOR_StackRelIndIndexY, + OpDef.OpMVN_BlockMove, + OpDef.OpEOR_DPIndexX, + OpDef.OpLSR_DPIndexX, + OpDef.OpEOR_DPIndIndexYLong, + OpDef.OpCLI_Implied, // 0x58 + OpDef.OpEOR_AbsIndexY, + OpDef.OpPHY_StackPush, + OpDef.OpTCD_Implied, + OpDef.OpJMP_AbsLong, + OpDef.OpEOR_AbsIndexX, + OpDef.OpLSR_AbsIndexX, + OpDef.OpEOR_AbsIndexXLong, + OpDef.OpRTS_StackRTS, // 0x60 + OpDef.OpADC_DPIndexXInd, + OpDef.OpPER_StackPCRelLong, + OpDef.OpADC_StackRel, + OpDef.OpSTZ_DP, + OpDef.OpADC_DP, + OpDef.OpROR_DP, + OpDef.OpADC_DPIndLong, + OpDef.OpPLA_StackPull, // 0x68 + OpDef.OpADC_ImmLongA, + OpDef.OpROR_Acc, + OpDef.OpRTL_StackRTL, + OpDef.OpJMP_AbsInd, + OpDef.OpADC_Abs, + OpDef.OpROR_Abs, + OpDef.OpADC_AbsLong, + OpDef.OpBVS_PCRel, // 0x70 + OpDef.OpADC_DPIndIndexY, + OpDef.OpADC_DPInd, + OpDef.OpADC_StackRelIndIndexY, + OpDef.OpSTZ_DPIndexX, + OpDef.OpADC_DPIndexX, + OpDef.OpROR_DPIndexX, + OpDef.OpADC_DPIndIndexYLong, + OpDef.OpSEI_Implied, // 0x78 + OpDef.OpADC_AbsIndexY, + OpDef.OpPLY_StackPull, + OpDef.OpTDC_Implied, + OpDef.OpJMP_AbsIndexXInd, + OpDef.OpADC_AbsIndexX, + OpDef.OpROR_AbsIndexX, + OpDef.OpADC_AbsIndexXLong, + OpDef.OpBRA_PCRel, // 0x80 + OpDef.OpSTA_DPIndexXInd, + OpDef.OpBRL_PCRelLong, + OpDef.OpSTA_StackRel, + OpDef.OpSTY_DP, + OpDef.OpSTA_DP, + OpDef.OpSTX_DP, + OpDef.OpSTA_DPIndLong, + OpDef.OpDEY_Implied, // 0x88 + OpDef.OpBIT_ImmLongA, + OpDef.OpTXA_Implied, + OpDef.OpPHB_StackPush, + OpDef.OpSTY_Abs, + OpDef.OpSTA_Abs, + OpDef.OpSTX_Abs, + OpDef.OpSTA_AbsLong, + OpDef.OpBCC_PCRel, // 0x90 + OpDef.OpSTA_DPIndIndexY, + OpDef.OpSTA_DPInd, + OpDef.OpSTA_StackRelIndIndexY, + OpDef.OpSTY_DPIndexX, + OpDef.OpSTA_DPIndexX, + OpDef.OpSTX_DPIndexY, + OpDef.OpSTA_DPIndIndexYLong, + OpDef.OpTYA_Implied, // 0x98 + OpDef.OpSTA_AbsIndexY, + OpDef.OpTXS_Implied, + OpDef.OpTXY_Implied, + OpDef.OpSTZ_Abs, + OpDef.OpSTA_AbsIndexX, + OpDef.OpSTZ_AbsIndexX, + OpDef.OpSTA_AbsIndexXLong, + OpDef.OpLDY_ImmLongXY, // 0xa0 + OpDef.OpLDA_DPIndexXInd, + OpDef.OpLDX_ImmLongXY, + OpDef.OpLDA_StackRel, + OpDef.OpLDY_DP, + OpDef.OpLDA_DP, + OpDef.OpLDX_DP, + OpDef.OpLDA_DPIndLong, + OpDef.OpTAY_Implied, // 0xa8 + OpDef.OpLDA_ImmLongA, + OpDef.OpTAX_Implied, + OpDef.OpPLB_StackPull, + OpDef.OpLDY_Abs, + OpDef.OpLDA_Abs, + OpDef.OpLDX_Abs, + OpDef.OpLDA_AbsLong, + OpDef.OpBCS_PCRel, // 0xb0 + OpDef.OpLDA_DPIndIndexY, + OpDef.OpLDA_DPInd, + OpDef.OpLDA_StackRelIndIndexY, + OpDef.OpLDY_DPIndexX, + OpDef.OpLDA_DPIndexX, + OpDef.OpLDX_DPIndexY, + OpDef.OpLDA_DPIndIndexYLong, + OpDef.OpCLV_Implied, // 0xb8 + OpDef.OpLDA_AbsIndexY, + OpDef.OpTSX_Implied, + OpDef.OpTYX_Implied, + OpDef.OpLDY_AbsIndexX, + OpDef.OpLDA_AbsIndexX, + OpDef.OpLDX_AbsIndexY, + OpDef.OpLDA_AbsIndexXLong, + OpDef.OpCPY_ImmLongXY, // 0xc0 + OpDef.OpCMP_DPIndexXInd, + OpDef.OpREP_Imm, + OpDef.OpCMP_StackRel, + OpDef.OpCPY_DP, + OpDef.OpCMP_DP, + OpDef.OpDEC_DP, + OpDef.OpCMP_DPIndLong, + OpDef.OpINY_Implied, // 0xc8 + OpDef.OpCMP_ImmLongA, + OpDef.OpDEX_Implied, + OpDef.OpWAI_Implied, + OpDef.OpCPY_Abs, + OpDef.OpCMP_Abs, + OpDef.OpDEC_Abs, + OpDef.OpCMP_AbsLong, + OpDef.OpBNE_PCRel, // 0xd0 + OpDef.OpCMP_DPIndIndexY, + OpDef.OpCMP_DPInd, + OpDef.OpCMP_StackRelIndIndexY, + OpDef.OpPEI_StackDPInd, + OpDef.OpCMP_DPIndexX, + OpDef.OpDEC_DPIndexX, + OpDef.OpCMP_DPIndIndexYLong, + OpDef.OpCLD_Implied, // 0xd8 + OpDef.OpCMP_AbsIndexY, + OpDef.OpPHX_StackPush, + OpDef.OpSTP_Implied, + OpDef.OpJMP_AbsIndLong, + OpDef.OpCMP_AbsIndexX, + OpDef.OpDEC_AbsIndexX, + OpDef.OpCMP_AbsIndexXLong, + OpDef.OpCPX_ImmLongXY, // 0xe0 + OpDef.OpSBC_DPIndexXInd, + OpDef.OpSEP_Imm, + OpDef.OpSBC_StackRel, + OpDef.OpCPX_DP, + OpDef.OpSBC_DP, + OpDef.OpINC_DP, + OpDef.OpSBC_DPIndLong, + OpDef.OpINX_Implied, // 0xe8 + OpDef.OpSBC_ImmLongA, + OpDef.OpNOP_Implied, + OpDef.OpXBA_Implied, + OpDef.OpCPX_Abs, + OpDef.OpSBC_Abs, + OpDef.OpINC_Abs, + OpDef.OpSBC_AbsLong, + OpDef.OpBEQ_PCRel, // 0xf0 + OpDef.OpSBC_DPIndIndexY, + OpDef.OpSBC_DPInd, + OpDef.OpSBC_StackRelIndIndexY, + OpDef.OpPEA_StackAbs, + OpDef.OpSBC_DPIndexX, + OpDef.OpINC_DPIndexX, + OpDef.OpSBC_DPIndIndexYLong, + OpDef.OpSED_Implied, // 0xf8 + OpDef.OpSBC_AbsIndexY, + OpDef.OpPLX_StackPull, + OpDef.OpXCE_Implied, + OpDef.OpJSR_AbsIndexXInd, + OpDef.OpSBC_AbsIndexX, + OpDef.OpINC_AbsIndexX, + OpDef.OpSBC_AbsIndexXLong + } + }; + } +} diff --git a/Asm65/Formatter.cs b/Asm65/Formatter.cs new file mode 100644 index 0000000..8f9398b --- /dev/null +++ b/Asm65/Formatter.cs @@ -0,0 +1,819 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Text; + +using AddressMode = Asm65.OpDef.AddressMode; + +namespace Asm65 { + /// + /// Functions used for formatting bits of 65xx code into human-readable form. + /// + /// There are a variety of ways to format a given thing, based on personal preference + /// (e.g. whether opcodes are upper- or lower-case) and assembler syntax requirements. + /// + /// The functions in this class serve two purposes: (1) produce consistent output + /// throughout the program; (2) cache format strings and other components to reduce + /// string manipulation overhead. Note the caching is per-Formatter, so it's best to + /// create just one and share it around. + /// + /// The configuration of a Formatter may not be altered once created. This is important + /// in situations where we compute output size in one pass and generate it in another, + /// because it guarantees that a given Formatter object will produce the same number of + /// lines of output. + /// + /// NOTE: if the CpuDef changes, the cached values in the Formatter will become invalid. + /// Discard the Formatter and create a new one. (This could be fixed by keying off of + /// the OpDef instead of OpDef.Opcode, but that's less convenient.) + /// + public class Formatter { + /// + /// Various format configuration options. Fill one of these out and pass it to + /// the Formatter constructor. + /// + public struct FormatConfig { + // alpha case for some case-insensitive items + public bool mUpperHexDigits; // display hex values in upper case? + public bool mUpperOpcodes; // display opcodes in upper case? + public bool mUpperPseudoOpcodes; // display pseudo-opcodes in upper case? + public bool mUpperOperandA; // display acc operand in upper case? + public bool mUpperOperandS; // display stack operand in upper case? + public bool mUpperOperandXY; // display index register operand in upper case? + public bool mAddSpaceLongComment; // insert space after delimiter for long comments? + + // functional changes to assembly output + public bool mSuppressHexNotation; // omit '$' before hex digits + + public bool mAllowHighAsciiCharConst; // can we do high-ASCII character constants? + // (this might need to be generalized) + + public string mForceAbsOpcodeSuffix; // these may be null or empty + public string mForceAbsOperandPrefix; + public string mForceLongOpcodeSuffix; + public string mForceLongOperandPrefix; + + public string mEndOfLineCommentDelimiter; // usually ';' + public string mFullLineCommentDelimiterBase; // usually ';' or '*', WITHOUT extra space + public string mBoxLineCommentDelimiter; // usually blank or ';' + + // miscellaneous + public bool mHexDumpAsciiOnly; // disallow non-ASCII chars in hex dumps? + + public enum CharConvMode { Unknown = 0, PlainAscii, HighLowAscii }; + public CharConvMode mHexDumpCharConvMode; // character conversion mode for dumps + + public enum ExpressionMode { Unknown = 0, Simple, Merlin }; + public ExpressionMode mExpressionMode; // symbol rendering mode + + // Deserialization helper. + public static ExpressionMode ParseExpressionMode(string str) { + ExpressionMode em = ExpressionMode.Simple; + if (!string.IsNullOrEmpty(str)) { + if (Enum.TryParse(str, out ExpressionMode pem)) { + em = pem; + } + } + return em; + } + } + + private static readonly char[] sHexCharsLower = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + private static readonly char[] sHexCharsUpper = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + /// + /// Formatter configuration options. Fixed at construction time. + /// + private FormatConfig mFormatConfig; + + /// + /// Get a copy of the format config. + /// + public FormatConfig Config { get { return mFormatConfig; } } + + // Bits and pieces. + char mHexFmtChar; + string mHexPrefix; + char mAccChar; + char mXregChar; + char mYregChar; + char mSregChar; + + // Format string for offsets. + private string mOffset20Format; + + // Format strings for addresses. + private string mAddrFormatNoBank; + private string mAddrFormatWithBank; + + // Generated opcode strings. The index is the bitwise OR of the opcode value and + // the disambiguation value. In most cases this just helps us avoid calling + // ToUpper incessantly. + private Dictionary mOpcodeStrings = new Dictionary(); + + // Generated pseudo-opcode strings. + private Dictionary mPseudoOpStrings = new Dictionary(); + + // Generated format strings for operands. The index is the bitwise OR of the + // address mode and the disambiguation value. + private Dictionary mOperandFormats = new Dictionary(); + + // Generated format strings for bytes. + private const int MAX_BYTE_DUMP = 4; + private string[] mByteDumpFormats = new string[MAX_BYTE_DUMP]; + + // Generated format strings for hex values. + private string[] mHexValueFormats = new string[4]; + + private string mFullLineCommentDelimiterPlus; + + // Buffer to use when generating hex dump lines. + private char[] mHexDumpBuffer; + + + /// + /// A 16-character array with 0-9a-f, for hex conversions. The letters will be + /// upper or lower case, per the format config. + /// + public char[] HexDigits { + get { + return mFormatConfig.mUpperHexDigits ? sHexCharsUpper : sHexCharsLower; + } + } + + /// + /// String to put between the operand and the end-of-line comment. + /// + public string EndOfLineCommentDelimiter { + get { return mFormatConfig.mEndOfLineCommentDelimiter; } + } + + /// + /// String to put at the start of a line with a full-line comment. + /// + public string FullLineCommentDelimiter { + get { return mFullLineCommentDelimiterPlus; } + } + + /// + /// String to put at the start of a line that has a box comment. This is usually + /// blank, as it's only needed if the assembler doesn't recognize the box character + /// as a comment. + /// + public string BoxLineCommentDelimiter { + get { return mFormatConfig.mBoxLineCommentDelimiter; } + } + + /// + /// When formatting a symbol with an offset, if this flag is set, generate code that + /// assumes the assembler applies the adjustment, then shifts the result. If not, + /// assume the assembler shifts the operand before applying the adjustment. + /// + public FormatConfig.ExpressionMode ExpressionMode { + get { return mFormatConfig.mExpressionMode; } + } + + + public Formatter(FormatConfig config) { + mFormatConfig = config; + if (mFormatConfig.mEndOfLineCommentDelimiter == null) { + mFormatConfig.mEndOfLineCommentDelimiter = string.Empty; + } + if (mFormatConfig.mFullLineCommentDelimiterBase == null) { + mFormatConfig.mFullLineCommentDelimiterBase = string.Empty; + } + if (mFormatConfig.mBoxLineCommentDelimiter == null) { + mFormatConfig.mBoxLineCommentDelimiter = string.Empty; + } + + if (mFormatConfig.mAddSpaceLongComment) { + mFullLineCommentDelimiterPlus = mFormatConfig.mFullLineCommentDelimiterBase + " "; + } else { + mFullLineCommentDelimiterPlus = mFormatConfig.mFullLineCommentDelimiterBase; + } + + Reset(); + + // Prep the static parts of the hex dump buffer. + mHexDumpBuffer = new char[73]; + for (int i = 0; i < mHexDumpBuffer.Length; i++) { + mHexDumpBuffer[i] = ' '; + } + mHexDumpBuffer[6] = ':'; + } + + /// + /// Resets the pieces we use to build format strings. + /// + private void Reset() { + // Clear old data. (No longer needed.) + //mAddrFormatNoBank = mAddrFormatWithBank = null; + //mOffset20Format = null; + //mOpcodeStrings.Clear(); + //mPseudoOpStrings.Clear(); + //mOperandFormats.Clear(); + //for (int i = 0; i < MAX_BYTE_DUMP; i++) { + // mByteDumpFormats[i] = null; + //} + + if (mFormatConfig.mUpperHexDigits) { + mHexFmtChar = 'X'; + } else { + mHexFmtChar = 'x'; + } + if (mFormatConfig.mSuppressHexNotation) { + mHexPrefix = ""; + } else { + mHexPrefix = "$"; + } + if (mFormatConfig.mUpperOperandA) { + mAccChar = 'A'; + } else { + mAccChar = 'a'; + } + if (mFormatConfig.mUpperOperandXY) { + mXregChar = 'X'; + mYregChar = 'Y'; + } else { + mXregChar = 'x'; + mYregChar = 'y'; + } + if (mFormatConfig.mUpperOperandS) { + mSregChar = 'S'; + } else { + mSregChar = 's'; + } + } + + /// + /// Formats a 24-bit offset value as hex. + /// + /// Offset to format. + /// Formatted string. + public string FormatOffset24(int offset) { + if (string.IsNullOrEmpty(mOffset20Format)) { + mOffset20Format = "+{0:" + mHexFmtChar + "6}"; + } + return string.Format(mOffset20Format, offset & 0x0fffff); + } + + /// + /// Formats a value in hexadecimal. The width is padded with zeroes to make the + /// length even (so it'll be $00, $0100, $010000, etc.) If minDigits is nonzero, + /// additional zeroes may be added. + /// + /// Value to format, up to 32 bits. + /// Minimum width, in printed digits (e.g. 4 is "0000"). + /// Formatted string. + public string FormatHexValue(int value, int minDigits) { + int width = minDigits > 2 ? minDigits : 2; + if (width < 8 && value > 0xffffff) { + width = 8; + } else if (width < 6 && value > 0xffff) { + width = 6; + } else if (width < 4 && value > 0xff) { + width = 4; + } + int index = (width / 2) - 1; + if (mHexValueFormats[index] == null) { + mHexValueFormats[index] = mHexFmtChar + width.ToString(); + } + return mHexPrefix + value.ToString(mHexValueFormats[index]); + } + + /// + /// Format a value as a number in the specified base. + /// + /// Value to format. + /// Numeric base (2, 10, or 16). + /// Formatted string. + public string FormatValueInBase(int value, int numBase) { + switch (numBase) { + case 2: + return FormatBinaryValue(value, 8); + case 10: + return FormatDecimalValue(value); + case 16: + return FormatHexValue(value, 2); + default: + Debug.Assert(false); + return "???"; + } + } + + /// + /// Formats a value as decimal. + /// + /// Value to convert. + /// Formatted string. + public string FormatDecimalValue(int value) { + return value.ToString(); + } + + /// + /// Formats a value in binary, padding with zeroes so the length is a multiple of 8. + /// + /// Value to convert. + /// Minimum width, in printed digits. Will be rounded up to + /// a multiple of 8. + /// Formatted string. + public string FormatBinaryValue(int value, int minDigits) { + string binaryStr = Convert.ToString(value, 2); + int desiredWidth = ((binaryStr.Length + 7) / 8) * 8; + if (desiredWidth < minDigits) { + desiredWidth = ((minDigits + 7) / 8) * 8; + } + return '%' + binaryStr.PadLeft(desiredWidth, '0'); + } + + /// + /// Formats a value as an ASCII character, surrounded by quotes. Must be a valid + /// low- or high-ASCII value. + /// + /// Value to format. + /// Formatted string. + private string FormatAsciiChar(int value) { + Debug.Assert(CommonUtil.TextUtil.IsHiLoAscii(value)); + char ch = (char)(value & 0x7f); + bool hiAscii = ((value & 0x80) != 0); + + StringBuilder sb; + int method = -1; + switch (method) { + case 0: + default: + // Convention is from Merlin: single quote for low-ASCII, double-quote + // for high-ASCII. Add a backslash if we're quoting the delimiter. + sb = new StringBuilder(4); + char quoteChar = ((value & 0x80) == 0) ? '\'' : '"'; + sb.Append(quoteChar); + if (quoteChar == ch) { + sb.Append('\\'); + } + sb.Append(ch); + sb.Append(quoteChar); + break; + case 1: + // Convention is similar to Merlin, but with curly-quotes so it doesn't + // look weird when quoting ' or ". + sb = new StringBuilder(3); + sb.Append(hiAscii ? '\u201c' : '\u2018'); + sb.Append(ch); + sb.Append(hiAscii ? '\u201d' : '\u2019'); + break; + case 2: + // Always use apostrophe, but follow it with an up-arrow to indicate + // that it's high-ASCII. + sb = new StringBuilder(4); + sb.Append("'"); + sb.Append(ch); + sb.Append("'"); + if (hiAscii) { + sb.Append('\u21e1'); // UPWARDS DASHED ARROW + //sb.Append('\u2912'); // UPWARDS ARROW TO BAR + } + break; + } + return sb.ToString(); + } + + /// + /// Formats a value as an ASCII character, if possible, or as a hex value. + /// + /// Value to format. + /// Formatted string. + public string FormatAsciiOrHex(int value) { + bool hiAscii = ((value & 0x80) != 0); + if (hiAscii && !mFormatConfig.mAllowHighAsciiCharConst) { + return FormatHexValue(value, 2); + } else if (CommonUtil.TextUtil.IsHiLoAscii(value)) { + return FormatAsciiChar(value); + } else { + return FormatHexValue(value, 2); + } + } + + /// + /// Formats a 16- or 24-bit address value. This is intended for the left column + /// of something (hex dump, code listing), not as an operand. + /// + /// Address to format. + /// Set to true for CPUs with 24-bit address spaces. + /// Formatted string. + public string FormatAddress(int address, bool showBank) { + if (mAddrFormatNoBank == null) { + mAddrFormatNoBank = "{0:" + mHexFmtChar + "4}"; + mAddrFormatWithBank = "{0:" + mHexFmtChar + "2}/{1:" + mHexFmtChar + "4}"; + } + if (showBank) { + return string.Format(mAddrFormatWithBank, address >> 16, address & 0xffff); + } else { + return string.Format(mAddrFormatNoBank, address & 0xffff); + } + } + + /// + /// Formats an adjustment, as "+decimal" or "-decimal". If no adjustment + /// is required, an empty string is returned. + /// + /// Adjustment value. + /// Formatted string. + public string FormatAdjustment(int adjValue) { + if (adjValue == 0) { + return string.Empty; + } + // This formats in decimal with a leading '+' or '-'. To avoid adding a plus + // on zero, we'd use "+#;-#;0", but we took care of the zero case above. + return adjValue.ToString("+0;-#"); + } + + /// + /// Formats the instruction opcode mnemonic, and caches the result. + /// + /// It may be necessary to modify the mnemonic for some assemblers, e.g. LDA from a + /// 24-bit address might need to be LDAL, even if the high byte is nonzero. + /// + /// Opcode to format + /// Width disambiguation specifier. + /// Formatted string. + public string FormatOpcode(OpDef op, OpDef.WidthDisambiguation wdis) { + // TODO(someday): using op.Opcode as the key is a bad idea, as the operation may + // not be the same on different CPUs. We currently rely on the caller to discard + // the Formatter when the CPU definition changes. We'd be better off keying off of + // the OpDef object and factoring wdis in some other way. + int key = op.Opcode | ((int)wdis << 8); + if (!mOpcodeStrings.TryGetValue(key, out string opcodeStr)) { + // Not found, generate value. + opcodeStr = FormatMnemonic(op.Mnemonic, wdis); + + // Memoize. + mOpcodeStrings[key] = opcodeStr; + } + return opcodeStr; + } + + /// + /// Formats the string as an opcode mnemonic. + /// + /// It may be necessary to modify the mnemonic for some assemblers, e.g. LDA from a + /// 24-bit address might need to be LDAL, even if the high byte is nonzero. + /// + /// Instruction mnemonic string. + /// Width disambiguation specifier. + /// + public string FormatMnemonic(string mnemonic, OpDef.WidthDisambiguation wdis) { + string opcodeStr = mnemonic; + if (wdis == OpDef.WidthDisambiguation.ForceAbs) { + if (!string.IsNullOrEmpty(mFormatConfig.mForceAbsOpcodeSuffix)) { + opcodeStr += mFormatConfig.mForceAbsOpcodeSuffix; + } + } else if (wdis == OpDef.WidthDisambiguation.ForceLong || + wdis == OpDef.WidthDisambiguation.ForceLongMaybe) { + if (!string.IsNullOrEmpty(mFormatConfig.mForceLongOpcodeSuffix)) { + opcodeStr += mFormatConfig.mForceLongOpcodeSuffix; + } + } else { + Debug.Assert(wdis == OpDef.WidthDisambiguation.None); + } + if (mFormatConfig.mUpperOpcodes) { + opcodeStr = opcodeStr.ToUpperInvariant(); + } + return opcodeStr; + } + + /// + /// Generates an operand format. + /// + /// Addressing mode. + /// Format string. + private string GenerateOperandFormat(OpDef.AddressMode addrMode, + OpDef.WidthDisambiguation wdis) { + string fmt; + string wdisStr = string.Empty; + + if (wdis == OpDef.WidthDisambiguation.ForceAbs) { + if (!string.IsNullOrEmpty(mFormatConfig.mForceAbsOperandPrefix)) { + wdisStr = mFormatConfig.mForceAbsOperandPrefix; + } + } else if (wdis == OpDef.WidthDisambiguation.ForceLong) { + if (!string.IsNullOrEmpty(mFormatConfig.mForceLongOperandPrefix)) { + wdisStr = mFormatConfig.mForceLongOperandPrefix; + } + } else if (wdis == OpDef.WidthDisambiguation.ForceLongMaybe) { + // Don't add a width disambiguator to an operand that is unambiguously long. + } else { + Debug.Assert(wdis == OpDef.WidthDisambiguation.None); + } + + switch (addrMode) { + case AddressMode.Abs: + case AddressMode.AbsLong: + case AddressMode.BlockMove: + case AddressMode.StackAbs: + case AddressMode.DP: + case AddressMode.PCRel: + case AddressMode.PCRelLong: // BRL + case AddressMode.StackInt: // BRK/COP + case AddressMode.StackPCRelLong: // PER + case AddressMode.WDM: + fmt = wdisStr + "{0}"; + break; + case AddressMode.AbsIndexX: + case AddressMode.AbsIndexXLong: + case AddressMode.DPIndexX: + fmt = wdisStr + "{0}," + mXregChar; + break; + case AddressMode.DPIndexY: + case AddressMode.AbsIndexY: + fmt = wdisStr + "{0}," + mYregChar; + break; + case AddressMode.AbsIndexXInd: + case AddressMode.DPIndexXInd: + fmt = wdisStr + "({0}," + mXregChar + ")"; + break; + case AddressMode.AbsInd: + case AddressMode.DPInd: + case AddressMode.StackDPInd: // PEI + fmt = "({0})"; + break; + case AddressMode.AbsIndLong: + case AddressMode.DPIndLong: + // IIgs monitor uses "()" for AbsIndLong, E&L says "[]". Assemblers + // seem to expect the latter. + fmt = "[{0}]"; + break; + case AddressMode.Acc: + fmt = "" + mAccChar; + break; + case AddressMode.DPIndIndexY: + fmt = "({0})," + mYregChar; + break; + case AddressMode.DPIndIndexYLong: + fmt = "[{0}]," + mYregChar; + break; + case AddressMode.Imm: + case AddressMode.ImmLongA: + case AddressMode.ImmLongXY: + fmt = "#{0}"; + break; + case AddressMode.Implied: + case AddressMode.StackPull: + case AddressMode.StackPush: + case AddressMode.StackRTI: + case AddressMode.StackRTL: + case AddressMode.StackRTS: + fmt = ""; + break; + case AddressMode.StackRel: + fmt = "{0}," + mSregChar; + break; + case AddressMode.StackRelIndIndexY: + fmt = "({0}," + mSregChar + ")," + mYregChar; + break; + + case AddressMode.Unknown: + default: + Debug.Assert(false); + fmt = "???"; + break; + } + + return fmt; + } + + /// + /// Formats a pseudo-opcode. + /// + /// Pseudo-op string to format. + /// Formatted string. + public string FormatPseudoOp(string opstr) { + if (!mPseudoOpStrings.TryGetValue(opstr, out string result)) { + if (mFormatConfig.mUpperPseudoOpcodes) { + result = mPseudoOpStrings[opstr] = opstr.ToUpperInvariant(); + } else { + result = mPseudoOpStrings[opstr] = opstr; + } + } + return result; + } + + /// + /// Formats the instruction operand. + /// + /// Opcode definition (needed for address mode). + /// Label or numeric operand value. + /// Width disambiguation value. + /// Formatted string. + public string FormatOperand(OpDef op, string contents, OpDef.WidthDisambiguation wdis) { + Debug.Assert(((int)op.AddrMode & 0xff) == (int) op.AddrMode); + int key = (int) op.AddrMode | ((int)wdis << 8); + + if (!mOperandFormats.TryGetValue(key, out string format)) { + format = mOperandFormats[key] = GenerateOperandFormat(op.AddrMode, wdis); + } + return string.Format(format, contents); + } + + /// + /// Generates a format string for N hex bytes. + /// + /// Number of bytes to handle in the format. + private void GenerateByteFormat(int len) { + Debug.Assert(len <= MAX_BYTE_DUMP); + + StringBuilder sb = new StringBuilder(len * 6); + for (int i = 0; i < len; i++) { + //. e.g. "{0:x2}" + sb.Append("{" + i + ":" + mHexFmtChar + "2}"); + } + mByteDumpFormats[len - 1] = sb.ToString(); + } + + /// + /// Formats 1-4 bytes as hex values. + /// + /// Data source. + /// Start offset within data array. + /// Number of bytes to print. Fewer than this many may + /// actually appear. + /// Formatted data string. + public string FormatBytes(byte[] data, int offset, int length) { + Debug.Assert(length > 0); + int printLen = length < MAX_BYTE_DUMP ? length : MAX_BYTE_DUMP; + if (string.IsNullOrEmpty(mByteDumpFormats[printLen - 1])) { + GenerateByteFormat(printLen); + } + string format = mByteDumpFormats[printLen - 1]; + string result; + + // The alternative is to allocate a temporary object[] and copy the integers + // into it, which requires boxing. We know we're only printing 1-4 bytes, so + // it's easier to just handle each case individually. + switch (printLen) { + case 1: + result = string.Format(format, data[offset]); + break; + case 2: + result = string.Format(format, data[offset], data[offset + 1]); + break; + case 3: + result = string.Format(format, + data[offset], data[offset + 1], data[offset + 2]); + break; + case 4: + result = string.Format(format, + data[offset], data[offset + 1], data[offset + 2], data[offset + 3]); + break; + default: + result = "INTERNAL ERROR"; + break; + } + if (length > printLen) { + result += "..."; + } + + return result; + } + + /// + /// Formats an end-of-line comment, prepending an end-of-line comment delimiter. + /// + /// Comment string; may be empty. + /// Formatted string. + public string FormatEolComment(string comment) { + if (string.IsNullOrEmpty(comment) || + string.IsNullOrEmpty(mFormatConfig.mEndOfLineCommentDelimiter)) { + return comment; + } else { + return mFormatConfig.mEndOfLineCommentDelimiter + comment; + } + } + + /// + /// Formats a collection of bytes as a dense hex string. + /// + /// Data source. + /// Start offset within data array. + /// Number of bytes to print. + /// Formatted data string. + public string FormatDenseHex(byte[] data, int offset, int length) { + char[] hexChars = mFormatConfig.mUpperHexDigits ? sHexCharsUpper : sHexCharsLower; + char[] text = new char[length * 2]; + for (int i = 0; i < length; i++) { + byte val = data[offset + i]; + text[i * 2] = hexChars[val >> 4]; + text[i * 2 + 1] = hexChars[val & 0x0f]; + } + return new string(text); + } + + /// + /// Formats up to 16 bytes of data into a single line hex dump, in this format: + ///
012345: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff  0123456789abcdef
+ ///
+ /// Reference to data. + /// Start offset. + /// Formatted string. + public string FormatHexDump(byte[] data, int offset) { + FormatHexDumpCommon(data, offset); + // this is the only allocation + return new string(mHexDumpBuffer); + } + + /// + /// Formats up to 16 bytes of data into a single line hex dump. The output is + /// appended to the StringBuilder. + /// + /// Reference to data. + /// Start offset. + /// StringBuilder that receives output. + public void FormatHexDump(byte[] data, int offset, StringBuilder sb) { + FormatHexDumpCommon(data, offset); + sb.Append(mHexDumpBuffer); + } + + /// + /// Formats up to 16 bytes of data into mHexDumpBuffer. + /// + private void FormatHexDumpCommon(byte[] data, int offset) { + Debug.Assert(offset >= 0 && offset < data.Length); + Debug.Assert(data.Length < (1 << 24)); + const int dataCol = 8; + const int asciiCol = 57; + + char[] hexChars = mFormatConfig.mUpperHexDigits ? sHexCharsUpper : sHexCharsLower; + char[] outBuf = mHexDumpBuffer; + + // address field + int addr = offset; + for (int i = 5; i >= 0; i--) { + outBuf[i] = hexChars[addr & 0x0f]; + addr >>= 4; + } + + // hex digits and characters + int length = Math.Min(16, data.Length - offset); + int index; + for (index = 0; index < length; index++) { + byte val = data[offset + index]; + outBuf[dataCol + index * 3] = hexChars[val >> 4]; + outBuf[dataCol + index * 3 + 1] = hexChars[val & 0x0f]; + outBuf[asciiCol + index] = CharConv(val); + } + + // for partial line, clear out previous contents + for (; index < 16; index++) { + outBuf[dataCol + index * 3] = + outBuf[dataCol + index * 3 + 1] = + outBuf[asciiCol + index] = ' '; + } + } + + /// + /// Converts a byte into printable form according to the current hex dump + /// character conversion mode. + /// + /// Value to convert. + /// Printable character. + private char CharConv(byte val) { + char ch; + if (mFormatConfig.mHexDumpCharConvMode == FormatConfig.CharConvMode.HighLowAscii) { + ch = (char)(val & 0x7f); + } else { + ch = (char)val; + } + if (CommonUtil.TextUtil.IsPrintableAscii(ch)) { + return ch; + } else if (mFormatConfig.mHexDumpAsciiOnly) { + return '.'; + } else { + // These values makes the hex dump ListView freak out. + //if (ch < 0x20) { + // return (char)(ch + '\u2400'); // Unicode "control pictures" block + //} + //return '\ufffd'; // Unicode "replacement character" + + //return '\u00bf'; // INVERTED QUESTION MARK + return '\u00b7'; // MIDDLE DOT + } + } + } +} diff --git a/Asm65/Helper.cs b/Asm65/Helper.cs new file mode 100644 index 0000000..e4954c5 --- /dev/null +++ b/Asm65/Helper.cs @@ -0,0 +1,56 @@ +/* + * Copyright 2018 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.Diagnostics; + +namespace Asm65 { + /// + /// Small utility functions. + /// + public static class Helper { + /// + /// Computes the target address of an 8-bit relative branch instruction. + /// + /// 24-bit address of branch instruction opcode. + /// Branch operand. + /// Target address. + public static int RelOffset8(int addr, sbyte branchOffset) { + // Branch is relative to the start of the following instruction, so add 2. + // Branches wrap around the current bank (in both directions), and the target + // is in the same bank as source addr. + Debug.Assert(addr >= 0 && addr <= 0xffffff); + int target = (addr + 2 + branchOffset) & 0xffff; + target |= addr & 0x7fff0000; + return target; + } + + /// + /// Computes the target address of a 16-bit relative branch instruction. + /// + /// 24-bit address of branch instruction opcode. + /// Branch operand. + /// Target address. + public static int RelOffset16(int addr, short branchOffset) { + // Branch is relative to the start of the following instruction, so add 3. + // Branches wrap around the current bank (in both directions), and the target + // is in the same bank as source addr. + Debug.Assert(addr >= 0 && addr <= 0xffffff); + int target = (addr + 3 + branchOffset) & 0xffff; + target |= addr & 0x7fff0000; + return target; + } + } +} diff --git a/Asm65/Label.cs b/Asm65/Label.cs new file mode 100644 index 0000000..3bc833c --- /dev/null +++ b/Asm65/Label.cs @@ -0,0 +1,78 @@ +/* + * Copyright 2018 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.Text.RegularExpressions; + +namespace Asm65 { + /// + /// Utility classes for working with labels. + /// + /// The decision of whether to treat labels as case-sensitive or case-insensitive is + /// encapsulated here. All code should be case-preserving, but the comparison method + /// and "normal form" are defined here. + /// + public class Label { + // Arbitrary choice for SourceGen. Different assemblers have different limits. + public const int MAX_LABEL_LEN = 32; + + public const bool LABELS_CASE_SENSITIVE = true; + + /// + /// String comparer to use when comparing labels. + /// + /// We may want case-insensitive string compares, and we want the "invariant culture" + /// version for consistent results across users in multiple locales. (The labels are + /// expected to be ASCII strings, so the latter isn't crucial unless we change the + /// allowed set.) + /// + public static readonly StringComparer LABEL_COMPARER = LABELS_CASE_SENSITIVE ? + StringComparer.InvariantCulture : + StringComparer.InvariantCultureIgnoreCase; + + /// + /// Regex pattern for a valid label. + /// + /// ASCII-only, starts with letter or underscore, followed by at least + /// one alphanumeric or underscore. Some assemblers may allow single-letter + /// labels, but I don't want to risk confusion with A/S/X/Y. So either we + /// reserve those, or we just mandate a two-character minimum. + /// + private static string sValidLabelPattern = @"^[a-zA-Z_][a-zA-Z0-9_]+$"; + private static Regex sValidLabelCharRegex = new Regex(sValidLabelPattern); + + /// + /// Validates a label, confirming that it is correctly formed. + /// + /// Label to validate. + /// True if the label is correctly formed. + public static bool ValidateLabel(string label) { + if (label.Length > MAX_LABEL_LEN) { + return false; + } + MatchCollection matches = sValidLabelCharRegex.Matches(label); + return matches.Count == 1; + } + + /// + /// Returns "normal form" of label. This matches LABEL_COMPARER behavior. + /// + /// Label to transform. + /// Transformed label. + public static string ToNormal(string label) { + return LABELS_CASE_SENSITIVE ? label : label.ToUpperInvariant(); + } + } +} diff --git a/Asm65/Number.cs b/Asm65/Number.cs new file mode 100644 index 0000000..ae97999 --- /dev/null +++ b/Asm65/Number.cs @@ -0,0 +1,60 @@ +/* + * Copyright 2018 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.Diagnostics; + +namespace Asm65 { + public class Number { + /// + /// Parses an integer in a variety of formats (hex, decimal, binary). + /// + /// Trim whitespace before calling here. + /// + /// String to parse. + /// Integer value of string. + /// What base the string was in (2, 10, or 16). + /// True if the parsing was successful. + public static bool TryParseInt(string str, out int val, out int intBase) { + if (string.IsNullOrEmpty(str)) { + val = intBase = 0; + return false; + } + + if (str[0] == '$') { + intBase = 16; + str = str.Substring(1); // Convert functions don't like '$' + } else if (str.Length > 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { + intBase = 16; + } else if (str[0] == '%') { + intBase = 2; + str = str.Substring(1); // Convert functions don't like '%' + } else { + intBase = 10; // try it as decimal + } + + try { + val = Convert.ToInt32(str, intBase); + //Debug.WriteLine("GOT " + val + " - " + intBase); + } catch (Exception) { + //Debug.WriteLine("TryParseInt failed on '" + str + "': " + ex.Message); + val = 0; + return false; + } + + return true; + } + } +} diff --git a/Asm65/OpDef.cs b/Asm65/OpDef.cs new file mode 100644 index 0000000..1463966 --- /dev/null +++ b/Asm65/OpDef.cs @@ -0,0 +1,3225 @@ +/* + * Copyright 2018 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.Diagnostics; + +namespace Asm65 { + /// + /// Operation code definitions for all 65xx-series CPUs. + /// + /// Instances are immutable. + /// + public class OpDef { + /// + /// Effect an instruction has on code flow. + /// + public enum FlowEffect { + Unknown = 0, + Branch, // JMP, BRA, BRL, ... (jump to new address specified in operand) + Cont, // LDA, STA, PHP, NOP, ... (always continue to next instruction) + NoCont, // RTS, BRK, ... (jump to new address, not specified in operand) + CallSubroutine, // JSR, JSL (jump to new address, and also continue to next) + ConditionalBranch // BCC, BEQ, ... (jump to new address and/or continue to next) + }; + + /// + /// Addressing mode. This uses the same distinctions as Eyes & Lichty, which for + /// most purposes has some redundancy (e.g. StackRTI is only ever used with RTI, so + /// in most cases it could be considered Implied). + /// + /// The mode, combined with the processor flags on the 65816, determines the number + /// of bytes required by the instruction. + /// + public enum AddressMode : byte { + Unknown = 0, + Abs, // OP addr 3 + AbsInd, // OP (addr) 3 (JMP) + AbsIndLong, // OP [addr] 3 (JML) + AbsIndexX, // OP addr,X 3 + AbsIndexXInd, // OP (addr,X) 3 (JMP/JSR) + AbsIndexXLong, // OP long,X 4 + AbsIndexY, // OP addr,Y 3 + AbsLong, // OP long 4 + Acc, // OP A 1 + BlockMove, // OP srcb,dstb 3 (MVN/MVP) + DP, // OP dp 2 + DPInd, // OP (dp) 2 + DPIndIndexY, // OP (dp),Y 2 + DPIndIndexYLong, // OP [dp],Y 2 + DPIndLong, // OP [dp] 2 + DPIndexX, // OP dp,X 2 + DPIndexXInd, // OP (dp,X) 2 + DPIndexY, // OP dp,Y 2 + Imm, // OP #const8 2 + ImmLongA, // OP #const8/16 2 or 3, depending on 'm' flag + ImmLongXY, // OP #const8/16 2 or 3, depending on 'x' flag + Implied, // OP 1 + PCRel, // OP label 2 (branch instructions) + PCRelLong, // OP label 3 (BRL) + StackAbs, // OP addr 3 (PEA) + StackDPInd, // OP (dp) 2 (PEI) + StackInt, // OP 2 (BRK, COP) + StackPCRelLong, // OP label 3 (PER) + StackPull, // OP 1 (PLA, PLX, ...) + StackPush, // OP 1 (PHA, PHX, ...) + StackRTI, // OP 1 (RTI) + StackRTL, // OP 1 (RTL) + StackRTS, // OP 1 (RTS) + StackRel, // OP sr,S 2 + StackRelIndIndexY, // OP (sr,S),Y 2 + WDM // OP 2? + } + + /// + /// Cycle count modifiers. This is meant to be bitwise-ORed with the base value. + /// + /// This defines things generally, with some modifiers noted as being specific to + /// certain CPUs. The actual instruction definitions may choose to include or + /// exclude bits according to the CPU being defined. + /// + [FlagsAttribute] + public enum CycleMod { + // Data from chapter 19 of Eyes & Lichty. + OneIfM0 = 1 << 8, // +1 if M=0 (16-bit mem/accumulator) + TwoIfM0 = 1 << 9, // +2 if M=0 (16-bit mem/accumulator) + OneIfX0 = 1 << 10, // +1 if X=0 (16-bit index regs) + OneIfDpNonzero = 1 << 11, // +1 if DP reg != 0 + OneIfIndexPage = 1 << 12, // +1 if indexing crosses page boundary + OneIfD1 = 1 << 13, // +1 if D=1 (decimal mode) ONLY on 65C02 + OneIfBranchTaken = 1 << 14, // +1 if conditional branch taken + OneIfBranchPage = 1 << 15, // +1 if branch crosses page UNLESS '816 native + OneIfE0 = 1 << 16, // +1 if 65816/865816 native mode (E=0) + OneIf65C02 = 1 << 17, // +1 if 65C02 + MinusOneIfNoPage = 1 << 18, // -1 if 65C02 and no page boundary crossed + BlockMove = 1 << 19, // +7 per byte moved + } + + /// + /// Width disambiguation requirement. + /// + public enum WidthDisambiguation : byte { + None = 0, + ForceAbs, + ForceLong, + ForceLongMaybe + + // May want an additional item: "force long if operand suffix specified". This + // would let us generate LDAL for assemblers that like to have that made explicit, + // while avoiding prepending it to operands that are unambiguously long (e.g. + // $ff1122). The counter-argument is that the operand prefix is still useful + // for humans when looking at labels, e.g. "a:FOO" vs. "f:FOO", because the value + // of the label may not be apparent. + } + + + /// + /// Opcode numeric value, e.g. BRK is $00. + /// + public byte Opcode { get; private set; } + + /// + /// Addressing mode. Determines length of instruction (mostly) and decoding of operand. + /// + public AddressMode AddrMode { get; private set; } + + /// + /// True if this is an undocumented opcode. + /// + public bool IsUndocumented { get; private set; } + + /// + /// Instruction mnemonic, e.g. LDA for Load Accumulator. + /// + public string Mnemonic { get; private set; } + + /// + /// Indication of which flags are affected by an instruction. Unaffected flags + /// will have the value TriState16.UNSPECIFIED. + /// + /// This is not equivalent to the code flow status flag updater -- this just notes which + /// flags are modified directly by the instruction. + /// + public StatusFlags FlagsAffected { get; private set; } + + /// + /// Effect this instruction has on code flow. + /// + public FlowEffect Effect { get; private set; } + + /// + /// Cycles required. The low 8 bits hold the base cycle count, the remaining bits + /// are defined by the CycleMod enum. + /// + private int CycDef { get; set; } + public int Cycles { get { return CycDef & 0xff; } } + public CycleMod CycleMods { get { return (CycleMod)(CycDef & ~0xff); } } + + /// + /// True if the operand's width is uniquely determined by the opcode mnemonic, even + /// if the operation supports operands with varying widths. + /// + /// Certain ops (ADC AND CMP EOR LDA ORA SBC STA) can be direct page, absolute, or + /// absolute long. "LDA 0" could mean LDA $00 (A5), LDA $0000 (AD), or LDA $000000 (AF). + /// Similar ambiguities exist for some indexed modes. Assemblers generally use the + /// smallest form, but allow a longer form to be selected by modifying the opcode + /// (e.g. LDA: and LDAL) or operand (e.g. LDA >0). + /// + /// Most operations that access memory require disambiguation for direct page vs. + /// absolute. Generally speaking, if the operand's high byte is empty, disambiguation + /// is required. + /// + /// The JMP/JML and JSR/JSL mnemonics are commonly used (and, in some assemblers, + /// required to be used) to avoid the ambiguity. For these instructions, we want + /// to avoid modifying the mnemonic, so we set this flag. + /// + private bool IsOperandWidthUnambiguous { get; set; } + + /// + /// Flag update delegate, used for code flow analysis. + /// + /// Previous flags value. + /// Immediate mode value, if any. Value may be 0-255 + /// for an 8-bit operand, or 0-65535 for a 16-bit operand, or -1 if this is + /// not an immediate-mode instruction. + /// New flags value. For conditional branches, this is + /// the value to use when the branch is not taken. + /// For conditional branches, this is the updated + /// value when the branch is taken. + private delegate StatusFlags FlagUpdater(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags); + private FlagUpdater StatusFlagUpdater { get; set; } + + /// + /// Nullary constructor. Most things are left at default values. + /// + public OpDef() { + StatusFlagUpdater = FlagUpdater_NoChange; + } + + /// + /// Copy constructor. + /// + /// Object to copy from + public OpDef(OpDef src) { + this.Opcode = src.Opcode; + this.AddrMode = src.AddrMode; + this.IsUndocumented = src.IsUndocumented; + this.Mnemonic = src.Mnemonic; + this.FlagsAffected = src.FlagsAffected; + this.Effect = src.Effect; + this.CycDef = src.CycDef; + this.IsOperandWidthUnambiguous = src.IsOperandWidthUnambiguous; + this.StatusFlagUpdater = src.StatusFlagUpdater; + } + + private static StatusFlags FlagsAffected_V = + new StatusFlags() { V = 1 }; + private static StatusFlags FlagsAffected_D = + new StatusFlags() { D = 1 }; + private static StatusFlags FlagsAffected_I = + new StatusFlags() { I = 1 }; + private static StatusFlags FlagsAffected_Z = + new StatusFlags() { Z = 1 }; + private static StatusFlags FlagsAffected_C = + new StatusFlags() { C = 1 }; + private static StatusFlags FlagsAffected_NZ = + new StatusFlags() { N = 1, Z = 1 }; + private static StatusFlags FlagsAffected_NZC = + new StatusFlags() { N = 1, Z = 1, C = 1 }; + private static StatusFlags FlagsAffected_NVZC = + new StatusFlags() { N = 1, V = 1, Z = 1, C = 1 }; + private static StatusFlags FlagsAffected_All = + new StatusFlags() { N = 1, V = 1, M = 1, X = 1, D = 1, I = 1, Z = 1, C = 1 }; + + + /// + /// True if this operation is a branch instruction (conditional branch, + /// unconditional branch/jump, subroutine call). + /// + public bool IsBranch { + get { + return Effect == FlowEffect.Branch || + Effect == FlowEffect.ConditionalBranch || + Effect == FlowEffect.CallSubroutine; + } + } + + /// + /// True if the operand is an immediate value, which should be prefixed with '#'. + /// + public bool IsImmediate { + get { + return AddrMode == AddressMode.Imm || + AddrMode == AddressMode.ImmLongA || + AddrMode == AddressMode.ImmLongXY; + } + } + + /// + /// True if the operand is an "extended immediate" value, which includes PEA + /// as well as Imm. + /// + public bool IsExtendedImmediate { + get { // should this include PER as well? + return IsImmediate || AddrMode == AddressMode.StackAbs; + } + } + + /// + /// True if the operand's width could be ambiguous. Generally speaking, assemblers + /// will use the shortest form possible, so disambiguation is about using a longer + /// form than may appear to be required. + /// + public bool IsWidthPotentiallyAmbiguous { + get { + if (IsOperandWidthUnambiguous) { + // JMP has Abs and AbsLong forms, but those are universally distinguished + // with unique mnemonics (JMP vs. JML). We don't want to generate "JMPL" + // or "JMP >long". Ditto for JSR/JSL. + return false; + } + switch (AddrMode) { + case AddressMode.Abs: // LDA $0000 vs LDA $00 + case AddressMode.AbsLong: // LDA $000000 vs LDA $0000/LDA $00 + case AddressMode.AbsIndexX: // LDA $0000,X vs LDA $00,X + case AddressMode.AbsIndexXLong: // LDA $000000,X vs LDA $0000,X/LDA $00,X + return true; + case AddressMode.AbsIndexY: // LDX $0000,Y vs LDX $00,Y + // AbsIndexY is widely used, but DPIndexY is only available for LDX/STX, + // and STX doesn't have AbsIndexY. So this is only ambiguous for LDX. + // We want to compare by opcode instance, rather than the byte code + // numeric value, to manage different instruction sets. + if (this == OpLDX_AbsIndexY) { + return true; + } + return false; + default: + return false; + } + } + } + + /// + /// Get a value that indicates what sort of disambiguation is required. Only call + /// this if IsWidthPotentiallyAmbiguous is true. + /// + /// Instruction width, including opcode. + /// Operand value, extracted from byte stream. + /// Width disambiguation value. + public static WidthDisambiguation GetWidthDisambiguation(int instrWidth, + int operandValue) { + Debug.Assert(instrWidth > 2 && instrWidth <= 4); // zero-page ops are not ambiguous + if (instrWidth == 3 && operandValue < 0x100) { + return WidthDisambiguation.ForceAbs; + } else if (instrWidth == 4) { + if (operandValue < 0x10000) { + return WidthDisambiguation.ForceLong; + } else { + // The width disambiguator may be helpful for humans when reading labels + // whose value is not immediately apparent. "LDA a:FOO" vs. "LDA f:FOO" + // could be nice. + return WidthDisambiguation.ForceLongMaybe; + } + } else { + return WidthDisambiguation.None; + } + } + + /// + /// Returns the full length of the instruction. Some 65816 operations use + /// a different number of bytes in 16-bit mode, so we need to know what the + /// M/X status flags are. + /// + /// Current status flags. + /// Length, in bytes. + public int GetLength(StatusFlags flags) { + switch (AddrMode) { + case AddressMode.Unknown: + case AddressMode.Acc: + case AddressMode.Implied: + case AddressMode.StackPull: + case AddressMode.StackPush: + case AddressMode.StackRTI: + case AddressMode.StackRTL: + case AddressMode.StackRTS: + return 1; + case AddressMode.DP: + case AddressMode.DPIndexX: + case AddressMode.DPIndexY: + case AddressMode.DPIndexXInd: + case AddressMode.DPInd: + case AddressMode.DPIndLong: + case AddressMode.DPIndIndexY: + case AddressMode.DPIndIndexYLong: + case AddressMode.Imm: + case AddressMode.PCRel: + case AddressMode.StackDPInd: + case AddressMode.StackInt: + case AddressMode.StackRel: + case AddressMode.StackRelIndIndexY: + case AddressMode.WDM: + return 2; + case AddressMode.Abs: + case AddressMode.AbsIndexX: + case AddressMode.AbsIndexY: + case AddressMode.AbsIndexXInd: + case AddressMode.AbsInd: + case AddressMode.AbsIndLong: + case AddressMode.BlockMove: + case AddressMode.PCRelLong: + case AddressMode.StackAbs: + case AddressMode.StackPCRelLong: + return 3; + + case AddressMode.AbsLong: + case AddressMode.AbsIndexXLong: + return 4; + + case AddressMode.ImmLongA: + bool shortM = flags.ShortM; + return shortM ? 2 : 3; + case AddressMode.ImmLongXY: + bool shortX = flags.ShortX; + return shortX ? 2 : 3; + + default: + Debug.Assert(false, "Unknown address mode " + AddrMode); + return -1; + } + } + + + /// + /// Return value from branch evaluation functions. + /// + public enum BranchTaken { Indeterminate = 0, Never, Always }; + + /// + /// Determines whether a branch is always taken, never taken, or is indeterminate + /// (i.e. it's taken some of the time but not taken others). + /// + /// Processor status flag value. + /// Bit value corresponding to branch-taken. + /// Branch taken indication. + private static BranchTaken FlagToBT(int flagVal, int branchVal) { + if (flagVal == TriState16.INDETERMINATE) { + return BranchTaken.Indeterminate; + } else if (flagVal == TriState16.UNSPECIFIED) { + // should never happen + Debug.Assert(false); + return BranchTaken.Indeterminate; + } else if (flagVal == branchVal) { + return BranchTaken.Always; + } else { + return BranchTaken.Never; + } + } + + /// + /// Determines if a conditional branch is always taken, based on the status flags. + /// + /// Conditional branch instruction. + /// Processor status flags. + /// Whether the branch is sometimes, always, or never taken. + public static BranchTaken IsBranchTaken(OpDef op, StatusFlags flags) { + // Could add a delegate to every OpDef, but that seems silly. + switch (op.Opcode) { + case 0x10: // BPL + return FlagToBT(flags.N, 0); + case 0x30: // BMI + return FlagToBT(flags.N, 1); + case 0x50: // BVC + return FlagToBT(flags.V, 0); + case 0x70: // BVS + return FlagToBT(flags.V, 1); + case 0x90: // BCC + return FlagToBT(flags.C, 0); + case 0xb0: // BCS + return FlagToBT(flags.C, 1); + case 0xd0: // BNE + return FlagToBT(flags.Z, 0); + case 0xf0: // BEQ + return FlagToBT(flags.Z, 1); + default: + // Not a conditional branch. + throw new Exception("Not a conditional branch"); + } + } + + /// + /// Get the raw operand value. + /// + /// 65xx code. + /// Offset of opcode. + /// Current status flags. + /// Operand value. + public int GetOperand(byte[] data, int offset, StatusFlags flags) { + switch (GetLength(flags)) { + case 1: + return -1; + case 2: + return data[offset + 1]; + case 3: + return (data[offset + 2] << 8) | data[offset + 1]; + case 4: + return (data[offset + 3] << 16) | (data[offset + 2] << 8) | data[offset + 1]; + default: + return -1; + } + } + + /// + /// Computes the changes the instruction will have on the status flags. + /// + /// Current status flags. This is used as the initial value + /// for the return values, and determines whether 65816 immediate operands are + /// treated as 8- or 16-bit. + /// 65xx data stream. + /// Offset of opcode. + /// Effect this instruction has on flags. + /// Effect this conditional branch instruction + /// has on flags when the branch is taken. If this is not a conditional branch, + /// the value can be ignored. + public void ComputeFlagChanges(StatusFlags curFlags, byte[] data, int offset, + out StatusFlags newFlags, out StatusFlags condBranchTakenFlags) { + int immVal = -1; + + switch (AddrMode) { + case AddressMode.Imm: + case AddressMode.ImmLongA: + case AddressMode.ImmLongXY: + if (GetLength(curFlags) == 2) { + // 8-bit operand + immVal = data[offset + 1]; + } else { + // 16-bit operand; make as such by negating it + // (if it's zero, 8 vs. 16 doesn't matter) + immVal = -((data[offset + 2]) << 16 | data[offset + 1]); + } + break; + default: + break; + } + + condBranchTakenFlags = curFlags; + // Invoke the flag update delegate. + newFlags = StatusFlagUpdater(curFlags, immVal, ref condBranchTakenFlags); + } + + private static StatusFlags FlagUpdater_NoChange(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + return flags; + } + private static StatusFlags FlagUpdater_Subroutine(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + // The correct way to do this is to merge the flags from all code + // that RTS/RTLs back to here, but that's generally hard to do, especially + // since this will often be used to call into external code. The easiest + // thing to do is scramble CZVN and leave IDXM alone. + return FlagUpdater_NVZC(flags, immVal, ref condBranchTakenFlags); + } + private static StatusFlags FlagUpdater_Z(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + flags.Z = TriState16.INDETERMINATE; + return flags; + } + private static StatusFlags FlagUpdater_C(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + flags.C = TriState16.INDETERMINATE; + return flags; + } + private static StatusFlags FlagUpdater_NZ(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + flags.Z = flags.N = TriState16.INDETERMINATE; + return flags; + } + private static StatusFlags FlagUpdater_NZC(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + flags.C = flags.Z = flags.N = TriState16.INDETERMINATE; + return flags; + } + private static StatusFlags FlagUpdater_NVZC(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + flags.C = flags.Z = flags.V = flags.N = TriState16.INDETERMINATE; + return flags; + } + private static StatusFlags FlagUpdater_LoadImm(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + // We can set the N/Z flags based on the value loaded. + flags.Z = (immVal != 0) ? 0 : 1; + if (immVal >= 0) { + // 8-bit operand + flags.N = (immVal >> 7) & 0x01; + } else { + // 16-bit operand + immVal = -immVal; + flags.N = (immVal >> 15) & 0x01; + } + return flags; + } + private static StatusFlags FlagUpdater_ANDImm(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + // AND #00 --> Z=1, else Z=prev + // AND #7f --> N=0, else N=prev + if (immVal == 0) { + flags.Z = 1; + } + bool hiBitClear; + if (immVal >= 0) { + // 8-bit operand + hiBitClear = ((immVal >> 7) & 0x01) == 0; + } else { + // 16-bit operand + immVal = -immVal; + hiBitClear = ((immVal >> 15) & 0x01) == 0; + } + if (hiBitClear) { + flags.N = 0; + } + return flags; + } + private static StatusFlags FlagUpdater_ORAImm(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + // ORA #00 --> Z=prev, else Z=0 + // ORA #80 --> N=1, else N=prev + if (immVal != 0) { + flags.Z = 0; + } + bool hiBitSet; + if (immVal >= 0) { + // 8-bit operand + hiBitSet = ((immVal >> 7) & 0x01) != 0; + } else { + // 16-bit operand + immVal = -immVal; + hiBitSet = ((immVal >> 15) & 0x01) != 0; + } + if (hiBitSet) { + flags.N = 1; + } + return flags; + } + private static StatusFlags FlagUpdater_PLP(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + // All flags are unknown. The caller may be able to match with a previous + // PHP to get something less fuzzy. + flags.C = flags.Z = flags.I = flags.D = flags.X = flags.M = flags.V = flags.N = + TriState16.INDETERMINATE; + return flags; + } + private static StatusFlags FlagUpdater_REP(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + if ((immVal & (1 << (int)StatusFlags.FlagBits.C)) != 0) { + flags.C = 0; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.Z)) != 0) { + flags.Z = 0; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.I)) != 0) { + flags.I = 0; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.D)) != 0) { + flags.D = 0; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.X)) != 0) { + flags.X = 0; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.M)) != 0) { + flags.M = 0; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.V)) != 0) { + flags.V = 0; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.N)) != 0) { + flags.N = 0; + } + return flags; + } + private static StatusFlags FlagUpdater_SEP(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + if ((immVal & (1 << (int)StatusFlags.FlagBits.C)) != 0) { + flags.C = 1; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.Z)) != 0) { + flags.Z = 1; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.I)) != 0) { + flags.I = 1; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.D)) != 0) { + flags.D = 1; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.X)) != 0) { + flags.X = 1; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.M)) != 0) { + flags.M = 1; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.V)) != 0) { + flags.V = 1; + } + if ((immVal & (1 << (int)StatusFlags.FlagBits.N)) != 0) { + flags.N = 1; + } + return flags; + } + private static StatusFlags FlagUpdater_XCE(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + // We want the 'E' flag to always have a definite value. If we don't know + // what's in the carry flag, guess it's a return to emulation. + StatusFlags newFlags = flags; + if (flags.C == 0) { + // transition to native + newFlags.E = 0; + newFlags.X = newFlags.M = 1; + } else /*C==1 or C==?*/ { + // transition to emulation + // The registers will be treated as short by the CPU, ignoring M/X, but + // the assembler won't generally know that. Set the flags to definite + // values here so we generate the necessary directives. + newFlags.E = 1; + newFlags.X = newFlags.M = 1; + } + newFlags.C = flags.E; + return newFlags; + } + + + // ====================================================================================== + // Base operation definitions, one per op. + // + + private static OpDef OpUnknown = new OpDef() { + Mnemonic = OpName.Unknown, + Effect = FlowEffect.Cont + }; + private static OpDef OpADC = new OpDef() { + Mnemonic = OpName.ADC, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NVZC, + StatusFlagUpdater = FlagUpdater_NVZC + }; + private static OpDef OpAND = new OpDef() { + Mnemonic = OpName.AND, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ // special handling for imm + }; + private static OpDef OpASL = new OpDef() { + Mnemonic = OpName.ASL, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, + StatusFlagUpdater = FlagUpdater_NZC + }; + private static OpDef OpBCC = new OpDef() { + Mnemonic = OpName.BCC, + Effect = FlowEffect.ConditionalBranch, + StatusFlagUpdater = delegate(StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + condBranchTakenFlags.C = 0; + flags.C = 1; + return flags; + } + }; + private static OpDef OpBCS = new OpDef() { + Mnemonic = OpName.BCS, + Effect = FlowEffect.ConditionalBranch, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + condBranchTakenFlags.C = 1; + flags.C = 0; + return flags; + } + }; + private static OpDef OpBEQ = new OpDef() { + Mnemonic = OpName.BEQ, + Effect = FlowEffect.ConditionalBranch, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + condBranchTakenFlags.Z = 1; + flags.Z = 0; + return flags; + } + }; + private static OpDef OpBIT = new OpDef() { + Mnemonic = OpName.BIT, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, // special handling for imm + StatusFlagUpdater = FlagUpdater_NZC // special handling for imm + }; + private static OpDef OpBMI = new OpDef() { + Mnemonic = OpName.BMI, + Effect = FlowEffect.ConditionalBranch, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + condBranchTakenFlags.N = 1; + flags.N = 0; + return flags; + } + }; + private static OpDef OpBNE = new OpDef() { + Mnemonic = OpName.BNE, + Effect = FlowEffect.ConditionalBranch, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + condBranchTakenFlags.Z = 0; + flags.Z = 1; + return flags; + } + }; + private static OpDef OpBPL = new OpDef() { + Mnemonic = OpName.BPL, + Effect = FlowEffect.ConditionalBranch, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + condBranchTakenFlags.N = 0; + flags.N = 1; + return flags; + } + }; + private static OpDef OpBRA = new OpDef() { + Mnemonic = OpName.BRA, + Effect = FlowEffect.Branch + }; + private static OpDef OpBRK = new OpDef() { + Mnemonic = OpName.BRK, + Effect = FlowEffect.NoCont + }; + private static OpDef OpBRL = new OpDef() { + Mnemonic = OpName.BRL, + Effect = FlowEffect.Branch + }; + private static OpDef OpBVC = new OpDef() { + Mnemonic = OpName.BVC, + Effect = FlowEffect.ConditionalBranch, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + condBranchTakenFlags.V = 0; + flags.V = 1; + return flags; + } + }; + private static OpDef OpBVS = new OpDef() { + Mnemonic = OpName.BVS, + Effect = FlowEffect.ConditionalBranch, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + condBranchTakenFlags.V = 1; + flags.V = 0; + return flags; + } + }; + private static OpDef OpCLC = new OpDef() { + Mnemonic = OpName.CLC, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_C, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + flags.C = 0; + return flags; + } + }; + private static OpDef OpCLD = new OpDef() { + Mnemonic = OpName.CLD, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_D, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + flags.D = 0; + return flags; + } + }; + private static OpDef OpCLI = new OpDef() { + Mnemonic = OpName.CLI, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_I, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + flags.I = 0; + return flags; + } + }; + private static OpDef OpCLV = new OpDef() { + Mnemonic = OpName.CLV, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_V, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + flags.V = 0; + return flags; + } + }; + private static OpDef OpCMP = new OpDef() { + Mnemonic = OpName.CMP, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, + StatusFlagUpdater = FlagUpdater_NZC + }; + private static OpDef OpCOP = new OpDef() { + Mnemonic = OpName.COP, + Effect = FlowEffect.Cont + }; + private static OpDef OpCPX = new OpDef() { + Mnemonic = OpName.CPX, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, + StatusFlagUpdater = FlagUpdater_NZC + }; + private static OpDef OpCPY = new OpDef() { + Mnemonic = OpName.CPY, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, + StatusFlagUpdater = FlagUpdater_NZC + }; + private static OpDef OpDEC = new OpDef() { + Mnemonic = OpName.DEC, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpDEX = new OpDef() { + Mnemonic = OpName.DEX, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpDEY = new OpDef() { + Mnemonic = OpName.DEY, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpEOR = new OpDef() { + Mnemonic = OpName.EOR, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpINC = new OpDef() { + Mnemonic = OpName.INC, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpINX = new OpDef() { + Mnemonic = OpName.INX, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpINY = new OpDef() { + Mnemonic = OpName.INY, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpJML = new OpDef() { + Mnemonic = OpName.JML, // technically JMP with long operand, but JML is convention + Effect = FlowEffect.Branch, + IsOperandWidthUnambiguous = true, + }; + private static OpDef OpJMP = new OpDef() { + Mnemonic = OpName.JMP, + Effect = FlowEffect.Branch, + IsOperandWidthUnambiguous = true + }; + private static OpDef OpJSL = new OpDef() { + Mnemonic = OpName.JSL, // technically JSR with long operand, but JSL is convention + Effect = FlowEffect.CallSubroutine, + IsOperandWidthUnambiguous = true, + StatusFlagUpdater = FlagUpdater_Subroutine + }; + private static OpDef OpJSR = new OpDef() { + Mnemonic = OpName.JSR, + Effect = FlowEffect.CallSubroutine, + IsOperandWidthUnambiguous = true, + StatusFlagUpdater = FlagUpdater_Subroutine + }; + private static OpDef OpLDA = new OpDef() { + Mnemonic = OpName.LDA, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ // special handling for imm + }; + private static OpDef OpLDX = new OpDef() { + Mnemonic = OpName.LDX, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ // special handling for imm + }; + private static OpDef OpLDY = new OpDef() { + Mnemonic = OpName.LDY, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ // special handling for imm + }; + private static OpDef OpLSR = new OpDef() { + Mnemonic = OpName.LSR, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, + StatusFlagUpdater = FlagUpdater_NZC + }; + private static OpDef OpMVN = new OpDef() { + Mnemonic = OpName.MVN, + Effect = FlowEffect.Cont + }; + private static OpDef OpMVP = new OpDef() { + Mnemonic = OpName.MVP, + Effect = FlowEffect.Cont + }; + private static OpDef OpNOP = new OpDef() { + Mnemonic = OpName.NOP, + Effect = FlowEffect.Cont + }; + private static OpDef OpORA = new OpDef() { + Mnemonic = OpName.ORA, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ // special handling for imm + }; + private static OpDef OpPEA = new OpDef() { + Mnemonic = OpName.PEA, + Effect = FlowEffect.Cont + }; + private static OpDef OpPEI = new OpDef() { + Mnemonic = OpName.PEI, + Effect = FlowEffect.Cont + }; + private static OpDef OpPER = new OpDef() { + Mnemonic = OpName.PER, + Effect = FlowEffect.Cont + }; + private static OpDef OpPHA = new OpDef() { + Mnemonic = OpName.PHA, + Effect = FlowEffect.Cont + }; + private static OpDef OpPHB = new OpDef() { + Mnemonic = OpName.PHB, + Effect = FlowEffect.Cont + }; + private static OpDef OpPHD = new OpDef() { + Mnemonic = OpName.PHD, + Effect = FlowEffect.Cont + }; + private static OpDef OpPHK = new OpDef() { + Mnemonic = OpName.PHK, + Effect = FlowEffect.Cont + }; + private static OpDef OpPHP = new OpDef() { + Mnemonic = OpName.PHP, + Effect = FlowEffect.Cont + }; + private static OpDef OpPHX = new OpDef() { + Mnemonic = OpName.PHX, + Effect = FlowEffect.Cont + }; + private static OpDef OpPHY = new OpDef() { + Mnemonic = OpName.PHY, + Effect = FlowEffect.Cont + }; + private static OpDef OpPLA = new OpDef() { + Mnemonic = OpName.PLA, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpPLB = new OpDef() { + Mnemonic = OpName.PLB, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpPLD = new OpDef() { + Mnemonic = OpName.PLD, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpPLP = new OpDef() { + Mnemonic = OpName.PLP, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_All, + StatusFlagUpdater = FlagUpdater_PLP + }; + private static OpDef OpPLX = new OpDef() { + Mnemonic = OpName.PLX, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpPLY = new OpDef() { + Mnemonic = OpName.PLY, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpREP = new OpDef() { + Mnemonic = OpName.REP, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_All, + StatusFlagUpdater = FlagUpdater_REP + }; + private static OpDef OpROL = new OpDef() { + Mnemonic = OpName.ROL, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, + StatusFlagUpdater = FlagUpdater_NZC + }; + private static OpDef OpROR = new OpDef() { + Mnemonic = OpName.ROR, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, + StatusFlagUpdater = FlagUpdater_NZC + }; + private static OpDef OpRTI = new OpDef() { + Mnemonic = OpName.RTI, + Effect = FlowEffect.NoCont + }; + private static OpDef OpRTL = new OpDef() { + Mnemonic = OpName.RTL, + Effect = FlowEffect.NoCont + }; + private static OpDef OpRTS = new OpDef() { + Mnemonic = OpName.RTS, + Effect = FlowEffect.NoCont + }; + private static OpDef OpSBC = new OpDef() { + Mnemonic = OpName.SBC, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NVZC, + StatusFlagUpdater = FlagUpdater_NVZC + }; + private static OpDef OpSEC = new OpDef() { + Mnemonic = OpName.SEC, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_C, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + flags.C = 1; + return flags; + } + }; + private static OpDef OpSED = new OpDef() { + Mnemonic = OpName.SED, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_D, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + flags.D = 1; + return flags; + } + }; + private static OpDef OpSEI = new OpDef() { + Mnemonic = OpName.SEI, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_I, + StatusFlagUpdater = delegate (StatusFlags flags, int immVal, + ref StatusFlags condBranchTakenFlags) { + flags.I = 1; + return flags; + } + }; + private static OpDef OpSEP = new OpDef() { + Mnemonic = OpName.SEP, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_All, + StatusFlagUpdater = FlagUpdater_SEP + }; + private static OpDef OpSTA = new OpDef() { + Mnemonic = OpName.STA, + Effect = FlowEffect.Cont + }; + private static OpDef OpSTP = new OpDef() { + Mnemonic = OpName.STP, + Effect = FlowEffect.NoCont + }; + private static OpDef OpSTX = new OpDef() { + Mnemonic = OpName.STX, + Effect = FlowEffect.Cont + }; + private static OpDef OpSTY = new OpDef() { + Mnemonic = OpName.STY, + Effect = FlowEffect.Cont + }; + private static OpDef OpSTZ = new OpDef() { + Mnemonic = OpName.STZ, + Effect = FlowEffect.Cont + }; + private static OpDef OpTAX = new OpDef() { + Mnemonic = OpName.TAX, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpTAY = new OpDef() { + Mnemonic = OpName.TAY, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpTCD = new OpDef() { + Mnemonic = OpName.TCD, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpTCS = new OpDef() { + Mnemonic = OpName.TCS, + Effect = FlowEffect.Cont + }; + private static OpDef OpTDC = new OpDef() { + Mnemonic = OpName.TDC, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpTRB = new OpDef() { + Mnemonic = OpName.TRB, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_Z, + StatusFlagUpdater = FlagUpdater_Z + }; + private static OpDef OpTSB = new OpDef() { + Mnemonic = OpName.TSB, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_Z, + StatusFlagUpdater = FlagUpdater_Z + }; + private static OpDef OpTSC = new OpDef() { + Mnemonic = OpName.TSC, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpTSX = new OpDef() { + Mnemonic = OpName.TSX, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpTXA = new OpDef() { + Mnemonic = OpName.TXA, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpTXS = new OpDef() { + Mnemonic = OpName.TXS, + Effect = FlowEffect.Cont + }; + private static OpDef OpTXY = new OpDef() { + Mnemonic = OpName.TXY, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpTYA = new OpDef() { + Mnemonic = OpName.TYA, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpTYX = new OpDef() { + Mnemonic = OpName.TYX, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpWAI = new OpDef() { + Mnemonic = OpName.WAI, + Effect = FlowEffect.Cont // when I=1 (interrupts disabled), continues on interrupt + }; + private static OpDef OpWDM = new OpDef() { + Mnemonic = OpName.WDM, + Effect = FlowEffect.Cont + }; + private static OpDef OpXBA = new OpDef() { + Mnemonic = OpName.XBA, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpXCE = new OpDef() { + Mnemonic = OpName.XCE, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_C, + StatusFlagUpdater = FlagUpdater_XCE + }; + + + #region 65816 Instructions + + // ====================================================================================== + // Operation + address mode definitions. + // + // It's possible for more than one definition to have the same opcode number. The + // 6502 only supports 8-bit LDA imm, while the 65816 version can be 8 or 16. There + // are minor differences in behavior for some opcodes on different CPUs. + // + // Significantly, the "invalid" 6502 ops overlap with official ops defined on later CPUs. + // + + public static readonly OpDef OpInvalid = new OpDef(OpUnknown) { + AddrMode = AddressMode.Unknown + }; + + public static readonly OpDef OpBRK_StackInt = new OpDef(OpBRK) { + Opcode = 0x00, + AddrMode = AddressMode.StackInt, + CycDef = 7 | (int)(CycleMod.OneIfE0) + }; + public static readonly OpDef OpORA_DPIndexXInd = new OpDef(OpORA) { + Opcode = 0x01, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpCOP_StackInt = new OpDef(OpCOP) { + Opcode = 0x02, + AddrMode = AddressMode.StackInt, + CycDef = 7 | (int)(CycleMod.OneIfE0) + }; + public static readonly OpDef OpORA_StackRel = new OpDef(OpORA) { + Opcode = 0x03, + AddrMode = AddressMode.StackRel, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpTSB_DP = new OpDef(OpTSB) { + Opcode = 0x04, + AddrMode = AddressMode.DP, + CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpORA_DP = new OpDef(OpORA) { + Opcode = 0x05, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpASL_DP = new OpDef(OpASL) { + Opcode = 0x06, + AddrMode = AddressMode.DP, + CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpORA_DPIndLong = new OpDef(OpORA) { + Opcode = 0x07, + AddrMode = AddressMode.DPIndLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpPHP_StackPush = new OpDef(OpPHP) { + Opcode = 0x08, + AddrMode = AddressMode.StackPush, + CycDef = 3 + }; + public static readonly OpDef OpORA_ImmLongA = new OpDef(OpORA) { + Opcode = 0x09, + AddrMode = AddressMode.ImmLongA, + StatusFlagUpdater = FlagUpdater_ORAImm, + CycDef = 2 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpORA_Imm = new OpDef(OpORA) { + Opcode = 0x09, + AddrMode = AddressMode.Imm, + StatusFlagUpdater = FlagUpdater_ORAImm, + CycDef = 2 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpASL_Acc = new OpDef(OpASL) { + Opcode = 0x0a, + AddrMode = AddressMode.Acc, + CycDef = 2 + }; + public static readonly OpDef OpPHD_StackPush = new OpDef(OpPHD) { + Opcode = 0x0b, + AddrMode = AddressMode.StackPush, + CycDef = 4 + }; + public static readonly OpDef OpTSB_Abs = new OpDef(OpTSB) { + Opcode = 0x0c, + AddrMode = AddressMode.Abs, + CycDef = 6 | (int)(CycleMod.TwoIfM0) + }; + public static readonly OpDef OpORA_Abs = new OpDef(OpORA) { + Opcode = 0x0d, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpASL_Abs = new OpDef(OpASL) { + Opcode = 0x0e, + AddrMode = AddressMode.Abs, + CycDef = 6 | (int)(CycleMod.TwoIfM0) + }; + public static readonly OpDef OpORA_AbsLong = new OpDef(OpORA) { + Opcode = 0x0f, + AddrMode = AddressMode.AbsLong, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpBPL_PCRel = new OpDef(OpBPL) { + Opcode = 0x10, + AddrMode = AddressMode.PCRel, + CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage) + }; + public static readonly OpDef OpORA_DPIndIndexY = new OpDef(OpORA) { + Opcode = 0x11, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpORA_DPInd = new OpDef(OpORA) { + Opcode = 0x12, + AddrMode = AddressMode.DPInd, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpORA_StackRelIndIndexY = new OpDef(OpORA) { + Opcode = 0x13, + AddrMode = AddressMode.StackRelIndIndexY, + CycDef = 7 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpTRB_DP = new OpDef(OpTRB) { + Opcode = 0x14, + AddrMode = AddressMode.DP, + CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpORA_DPIndexX = new OpDef(OpORA) { + Opcode = 0x15, + AddrMode = AddressMode.DPIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpASL_DPIndexX = new OpDef(OpASL) { + Opcode = 0x16, + AddrMode = AddressMode.DPIndexX, + CycDef = 6 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpORA_DPIndIndexYLong = new OpDef(OpORA) { + Opcode = 0x17, + AddrMode = AddressMode.DPIndIndexYLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpCLC_Implied = new OpDef(OpCLC) { + Opcode = 0x18, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpORA_AbsIndexY = new OpDef(OpORA) { + Opcode = 0x19, + AddrMode = AddressMode.AbsIndexY, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpINC_Acc = new OpDef(OpINC) { + Opcode = 0x1a, + AddrMode = AddressMode.Acc, + CycDef = 2 + }; + public static readonly OpDef OpTCS_Implied = new OpDef(OpTCS) { + Opcode = 0x1b, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpTRB_Abs = new OpDef(OpTRB) { + Opcode = 0x1c, + AddrMode = AddressMode.Abs, + CycDef = 6 | (int)(CycleMod.TwoIfM0) + }; + public static readonly OpDef OpORA_AbsIndexX = new OpDef(OpORA) { + Opcode = 0x1d, + AddrMode = AddressMode.AbsIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpASL_AbsIndexX = new OpDef(OpASL) { + Opcode = 0x1e, + AddrMode = AddressMode.AbsIndexX, + CycDef = 7 | (int)(CycleMod.TwoIfM0 | CycleMod.MinusOneIfNoPage) + }; + public static readonly OpDef OpORA_AbsIndexXLong = new OpDef(OpORA) { + Opcode = 0x1f, + AddrMode = AddressMode.AbsIndexXLong, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpJSR_Abs = new OpDef(OpJSR) { + Opcode = 0x20, + AddrMode = AddressMode.Abs, + CycDef = 6 + }; + public static readonly OpDef OpAND_DPIndexXInd = new OpDef(OpAND) { + Opcode = 0x21, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpJSR_AbsLong = new OpDef(OpJSL) { + Opcode = 0x22, + AddrMode = AddressMode.AbsLong, + CycDef = 8 + }; + public static readonly OpDef OpAND_StackRel = new OpDef(OpAND) { + Opcode = 0x23, + AddrMode = AddressMode.StackRel, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpBIT_DP = new OpDef(OpBIT) { + Opcode = 0x24, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpAND_DP = new OpDef(OpAND) { + Opcode = 0x25, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpROL_DP = new OpDef(OpROL) { + Opcode = 0x26, + AddrMode = AddressMode.DP, + CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpAND_DPIndLong = new OpDef(OpAND) { + Opcode = 0x27, + AddrMode = AddressMode.DPIndLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpPLP_StackPull = new OpDef(OpPLP) { + Opcode = 0x28, + AddrMode = AddressMode.StackPull, + CycDef = 4 + }; + public static readonly OpDef OpAND_ImmLongA = new OpDef(OpAND) { + Opcode = 0x29, + AddrMode = AddressMode.ImmLongA, + StatusFlagUpdater = FlagUpdater_ANDImm, + CycDef = 2 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpAND_Imm = new OpDef(OpAND) { + Opcode = 0x29, + AddrMode = AddressMode.Imm, + StatusFlagUpdater = FlagUpdater_ANDImm, + CycDef = 2 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpROL_Acc = new OpDef(OpROL) { + Opcode = 0x2a, + AddrMode = AddressMode.Acc, + CycDef = 2 + }; + public static readonly OpDef OpPLD_StackPull = new OpDef(OpPLD) { + Opcode = 0x2b, + AddrMode = AddressMode.StackPull, + CycDef = 5 + }; + public static readonly OpDef OpBIT_Abs = new OpDef(OpBIT) { + Opcode = 0x2c, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpAND_Abs = new OpDef(OpAND) { + Opcode = 0x2d, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpROL_Abs = new OpDef(OpROL) { + Opcode = 0x2e, + AddrMode = AddressMode.Abs, + CycDef = 6 | (int)(CycleMod.TwoIfM0) + }; + public static readonly OpDef OpAND_AbsLong = new OpDef(OpAND) { + Opcode = 0x2f, + AddrMode = AddressMode.AbsLong, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpBMI_PCRel = new OpDef(OpBMI) { + Opcode = 0x30, + AddrMode = AddressMode.PCRel, + CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage) + }; + public static readonly OpDef OpAND_DPIndIndexY = new OpDef(OpAND) { + Opcode = 0x31, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpAND_DPInd = new OpDef(OpAND) { + Opcode = 0x32, + AddrMode = AddressMode.DPInd, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpAND_StackRelIndIndexY = new OpDef(OpAND) { + Opcode = 0x33, + AddrMode = AddressMode.StackRelIndIndexY, + CycDef = 7 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpBIT_DPIndexX = new OpDef(OpBIT) { + Opcode = 0x34, + AddrMode = AddressMode.DPIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpAND_DPIndexX = new OpDef(OpAND) { + Opcode = 0x35, + AddrMode = AddressMode.DPIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpROL_DPIndexX = new OpDef(OpROL) { + Opcode = 0x36, + AddrMode = AddressMode.DPIndexX, + CycDef = 6 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpAND_DPIndIndexYLong = new OpDef(OpAND) { + Opcode = 0x37, + AddrMode = AddressMode.DPIndIndexYLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpSEC_Implied = new OpDef(OpSEC) { + Opcode = 0x38, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpAND_AbsIndexY = new OpDef(OpAND) { + Opcode = 0x39, + AddrMode = AddressMode.AbsIndexY, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpDEC_Acc = new OpDef(OpDEC) { + Opcode = 0x3a, + AddrMode = AddressMode.Acc, + CycDef = 2 + }; + public static readonly OpDef OpTSC_Implied = new OpDef(OpTSC) { + Opcode = 0x3b, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpBIT_AbsIndexX = new OpDef(OpBIT) { + Opcode = 0x3c, + AddrMode = AddressMode.AbsIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpAND_AbsIndexX = new OpDef(OpAND) { + Opcode = 0x3d, + AddrMode = AddressMode.AbsIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpROL_AbsIndexX = new OpDef(OpROL) { + Opcode = 0x3e, + AddrMode = AddressMode.AbsIndexX, + CycDef = 7 | (int)(CycleMod.TwoIfM0 | CycleMod.MinusOneIfNoPage) + }; + public static readonly OpDef OpAND_AbsIndexXLong = new OpDef(OpAND) { + Opcode = 0x3f, + AddrMode = AddressMode.AbsIndexXLong, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpRTI_StackRTI = new OpDef(OpRTI) { + Opcode = 0x40, + AddrMode = AddressMode.StackRTI, + CycDef = 6 | (int)(CycleMod.OneIfE0) + }; + public static readonly OpDef OpEOR_DPIndexXInd = new OpDef(OpEOR) { + Opcode = 0x41, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpWDM_WDM = new OpDef(OpWDM) { + Opcode = 0x42, + AddrMode = AddressMode.WDM, + CycDef = 2 // arbitrary; actual time is undefined + }; + public static readonly OpDef OpEOR_StackRel = new OpDef(OpEOR) { + Opcode = 0x43, + AddrMode = AddressMode.StackRel, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpMVP_BlockMove = new OpDef(OpMVP) { + Opcode = 0x44, + AddrMode = AddressMode.BlockMove, + CycDef = 7 | (int)(CycleMod.BlockMove) + }; + public static readonly OpDef OpEOR_DP = new OpDef(OpEOR) { + Opcode = 0x45, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpLSR_DP = new OpDef(OpLSR) { + Opcode = 0x46, + AddrMode = AddressMode.DP, + CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpEOR_DPIndLong = new OpDef(OpEOR) { + Opcode = 0x47, + AddrMode = AddressMode.DPIndLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpPHA_StackPush = new OpDef(OpPHA) { + Opcode = 0x48, + AddrMode = AddressMode.StackPush, + CycDef = 3 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpEOR_ImmLongA = new OpDef(OpEOR) { + Opcode = 0x49, + AddrMode = AddressMode.ImmLongA, + CycDef = 2 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpEOR_Imm = new OpDef(OpEOR) { + Opcode = 0x49, + AddrMode = AddressMode.Imm, + CycDef = 2 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpLSR_Acc = new OpDef(OpLSR) { + Opcode = 0x4a, + AddrMode = AddressMode.Acc, + CycDef = 2 + }; + public static readonly OpDef OpPHK_StackPush = new OpDef(OpPHK) { + Opcode = 0x4b, + AddrMode = AddressMode.StackPush, + CycDef = 3 + }; + public static readonly OpDef OpJMP_Abs = new OpDef(OpJMP) { + Opcode = 0x4c, + AddrMode = AddressMode.Abs, + CycDef = 3 + }; + public static readonly OpDef OpEOR_Abs = new OpDef(OpEOR) { + Opcode = 0x4d, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpLSR_Abs = new OpDef(OpLSR) { + Opcode = 0x4e, + AddrMode = AddressMode.Abs, + CycDef = 6 | (int)(CycleMod.TwoIfM0) + }; + public static readonly OpDef OpEOR_AbsLong = new OpDef(OpEOR) { + Opcode = 0x4f, + AddrMode = AddressMode.AbsLong, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpBVC_PCRel = new OpDef(OpBVC) { + Opcode = 0x50, + AddrMode = AddressMode.PCRel, + CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage) + }; + public static readonly OpDef OpEOR_DPIndIndexY = new OpDef(OpEOR) { + Opcode = 0x51, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpEOR_DPInd = new OpDef(OpEOR) { + Opcode = 0x52, + AddrMode = AddressMode.DPInd, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpEOR_StackRelIndIndexY = new OpDef(OpEOR) { + Opcode = 0x53, + AddrMode = AddressMode.StackRelIndIndexY, + CycDef = 7 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpMVN_BlockMove = new OpDef(OpMVN) { + Opcode = 0x54, + AddrMode = AddressMode.BlockMove, + CycDef = 7 | (int)(CycleMod.BlockMove) + }; + public static readonly OpDef OpEOR_DPIndexX = new OpDef(OpEOR) { + Opcode = 0x55, + AddrMode = AddressMode.DPIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpLSR_DPIndexX = new OpDef(OpLSR) { + Opcode = 0x56, + AddrMode = AddressMode.DPIndexX, + CycDef = 6 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpEOR_DPIndIndexYLong = new OpDef(OpEOR) { + Opcode = 0x57, + AddrMode = AddressMode.DPIndIndexYLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpCLI_Implied = new OpDef(OpCLI) { + Opcode = 0x58, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpEOR_AbsIndexY = new OpDef(OpEOR) { + Opcode = 0x59, + AddrMode = AddressMode.AbsIndexY, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpPHY_StackPush = new OpDef(OpPHY) { + Opcode = 0x5a, + AddrMode = AddressMode.StackPush, + CycDef = 3 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpTCD_Implied = new OpDef(OpTCD) { + Opcode = 0x5b, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpJMP_AbsLong = new OpDef(OpJML) { + Opcode = 0x5c, + AddrMode = AddressMode.AbsLong, + CycDef = 4 + }; + public static readonly OpDef OpEOR_AbsIndexX = new OpDef(OpEOR) { + Opcode = 0x5d, + AddrMode = AddressMode.AbsIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpLSR_AbsIndexX = new OpDef(OpLSR) { + Opcode = 0x5e, + AddrMode = AddressMode.AbsIndexX, + CycDef = 7 | (int)(CycleMod.TwoIfM0 | CycleMod.MinusOneIfNoPage) + }; + public static readonly OpDef OpEOR_AbsIndexXLong = new OpDef(OpEOR) { + Opcode = 0x5f, + AddrMode = AddressMode.AbsIndexXLong, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpRTS_StackRTS = new OpDef(OpRTS) { + Opcode = 0x60, + AddrMode = AddressMode.StackRTS, + CycDef = 6 + }; + public static readonly OpDef OpADC_DPIndexXInd = new OpDef(OpADC) { + Opcode = 0x61, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1) + }; + public static readonly OpDef OpPER_StackPCRelLong = new OpDef(OpPER) { + Opcode = 0x62, + AddrMode = AddressMode.StackPCRelLong, + CycDef = 6 + }; + public static readonly OpDef OpADC_StackRel = new OpDef(OpADC) { + Opcode = 0x63, + AddrMode = AddressMode.StackRel, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + public static readonly OpDef OpSTZ_DP = new OpDef(OpSTZ) { + Opcode = 0x64, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpADC_DP = new OpDef(OpADC) { + Opcode = 0x65, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1) + }; + public static readonly OpDef OpROR_DP = new OpDef(OpROR) { + Opcode = 0x66, + AddrMode = AddressMode.DP, + CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpADC_DPIndLong = new OpDef(OpADC) { + Opcode = 0x67, + AddrMode = AddressMode.DPIndLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1) + }; + public static readonly OpDef OpPLA_StackPull = new OpDef(OpPLA) { + Opcode = 0x68, + AddrMode = AddressMode.StackPull, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpADC_ImmLongA = new OpDef(OpADC) { + Opcode = 0x69, + AddrMode = AddressMode.ImmLongA, + CycDef = 2 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + public static readonly OpDef OpADC_Imm = new OpDef(OpADC) { + Opcode = 0x69, + AddrMode = AddressMode.Imm, + CycDef = 2 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + public static readonly OpDef OpROR_Acc = new OpDef(OpROR) { + Opcode = 0x6a, + AddrMode = AddressMode.Acc, + CycDef = 2 + }; + public static readonly OpDef OpRTL_StackRTL = new OpDef(OpRTL) { + Opcode = 0x6b, + AddrMode = AddressMode.StackRTL, + CycDef = 6 + }; + public static readonly OpDef OpJMP_AbsInd = new OpDef(OpJMP) { + Opcode = 0x6c, + AddrMode = AddressMode.AbsInd, + CycDef = 5 | (int)(CycleMod.OneIf65C02) + }; + public static readonly OpDef OpADC_Abs = new OpDef(OpADC) { + Opcode = 0x6d, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + public static readonly OpDef OpROR_Abs = new OpDef(OpROR) { + Opcode = 0x6e, + AddrMode = AddressMode.Abs, + CycDef = 6 | (int)(CycleMod.TwoIfM0) + }; + public static readonly OpDef OpADC_AbsLong = new OpDef(OpADC) { + Opcode = 0x6f, + AddrMode = AddressMode.AbsLong, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + public static readonly OpDef OpBVS_PCRel = new OpDef(OpBVS) { + Opcode = 0x70, + AddrMode = AddressMode.PCRel, + CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage) + }; + public static readonly OpDef OpADC_DPIndIndexY = new OpDef(OpADC) { + Opcode = 0x71, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | + CycleMod.OneIfIndexPage | CycleMod.OneIfD1) + }; + public static readonly OpDef OpADC_DPInd = new OpDef(OpADC) { + Opcode = 0x72, + AddrMode = AddressMode.DPInd, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1) + }; + public static readonly OpDef OpADC_StackRelIndIndexY = new OpDef(OpADC) { + Opcode = 0x73, + AddrMode = AddressMode.StackRelIndIndexY, + CycDef = 7 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + public static readonly OpDef OpSTZ_DPIndexX = new OpDef(OpSTZ) { + Opcode = 0x74, + AddrMode = AddressMode.DPIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpADC_DPIndexX = new OpDef(OpADC) { + Opcode = 0x75, + AddrMode = AddressMode.DPIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1) + }; + public static readonly OpDef OpROR_DPIndexX = new OpDef(OpROR) { + Opcode = 0x76, + AddrMode = AddressMode.DPIndexX, + CycDef = 6 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpADC_DPIndIndexYLong = new OpDef(OpADC) { + Opcode = 0x77, + AddrMode = AddressMode.DPIndIndexYLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1) + }; + public static readonly OpDef OpSEI_Implied = new OpDef(OpSEI) { + Opcode = 0x78, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpADC_AbsIndexY = new OpDef(OpADC) { + Opcode = 0x79, + AddrMode = AddressMode.AbsIndexY, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage | CycleMod.OneIfD1) + }; + public static readonly OpDef OpPLY_StackPull = new OpDef(OpPLY) { + Opcode = 0x7a, + AddrMode = AddressMode.StackPull, + CycDef = 4 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpTDC_Implied = new OpDef(OpTDC) { + Opcode = 0x7b, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpJMP_AbsIndexXInd = new OpDef(OpJMP) { + Opcode = 0x7c, + AddrMode = AddressMode.AbsIndexXInd, + CycDef = 6 + }; + public static readonly OpDef OpADC_AbsIndexX = new OpDef(OpADC) { + Opcode = 0x7d, + AddrMode = AddressMode.AbsIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage | CycleMod.OneIfD1) + }; + public static readonly OpDef OpROR_AbsIndexX = new OpDef(OpROR) { + Opcode = 0x7e, + AddrMode = AddressMode.AbsIndexX, + CycDef = 7 | (int)(CycleMod.TwoIfM0 | CycleMod.MinusOneIfNoPage) + }; + public static readonly OpDef OpADC_AbsIndexXLong = new OpDef(OpADC) { + Opcode = 0x7f, + AddrMode = AddressMode.AbsIndexXLong, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + public static readonly OpDef OpBRA_PCRel = new OpDef(OpBRA) { + Opcode = 0x80, + AddrMode = AddressMode.PCRel, + CycDef = 3 | (int)(CycleMod.OneIfBranchPage) + }; + public static readonly OpDef OpSTA_DPIndexXInd = new OpDef(OpSTA) { + Opcode = 0x81, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpBRL_PCRelLong = new OpDef(OpBRL) { + Opcode = 0x82, + AddrMode = AddressMode.PCRelLong, + CycDef = 4 + }; + public static readonly OpDef OpSTA_StackRel = new OpDef(OpSTA) { + Opcode = 0x83, + AddrMode = AddressMode.StackRel, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpSTY_DP = new OpDef(OpSTY) { + Opcode = 0x84, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0) + }; + public static readonly OpDef OpSTA_DP = new OpDef(OpSTA) { + Opcode = 0x85, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpSTX_DP = new OpDef(OpSTX) { + Opcode = 0x86, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0) + }; + public static readonly OpDef OpSTA_DPIndLong = new OpDef(OpSTA) { + Opcode = 0x87, + AddrMode = AddressMode.DPIndLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpDEY_Implied = new OpDef(OpDEY) { + Opcode = 0x88, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpBIT_ImmLongA = new OpDef(OpBIT) { + Opcode = 0x89, + AddrMode = AddressMode.ImmLongA, + FlagsAffected = FlagsAffected_Z, // special case + StatusFlagUpdater = FlagUpdater_Z, + CycDef = 2 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpBIT_Imm = new OpDef(OpBIT) { + Opcode = 0x89, + AddrMode = AddressMode.Imm, + FlagsAffected = FlagsAffected_Z, // special case + StatusFlagUpdater = FlagUpdater_Z, + CycDef = 2 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpTXA_Implied = new OpDef(OpTXA) { + Opcode = 0x8a, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpPHB_StackPush = new OpDef(OpPHB) { + Opcode = 0x8b, + AddrMode = AddressMode.StackPush, + CycDef = 3 + }; + public static readonly OpDef OpSTY_Abs = new OpDef(OpSTY) { + Opcode = 0x8c, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpSTA_Abs = new OpDef(OpSTA) { + Opcode = 0x8d, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpSTX_Abs = new OpDef(OpSTX) { + Opcode = 0x8e, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpSTA_AbsLong = new OpDef(OpSTA) { + Opcode = 0x8f, + AddrMode = AddressMode.AbsLong, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpBCC_PCRel = new OpDef(OpBCC) { + Opcode = 0x90, + AddrMode = AddressMode.PCRel, + CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage) + }; + public static readonly OpDef OpSTA_DPIndIndexY = new OpDef(OpSTA) { + Opcode = 0x91, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpSTA_DPInd = new OpDef(OpSTA) { + Opcode = 0x92, + AddrMode = AddressMode.DPInd, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpSTA_StackRelIndIndexY = new OpDef(OpSTA) { + Opcode = 0x93, + AddrMode = AddressMode.StackRelIndIndexY, + CycDef = 7 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpSTY_DPIndexX = new OpDef(OpSTY) { + Opcode = 0x94, + AddrMode = AddressMode.DPIndexX, + CycDef = 4 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0) + }; + public static readonly OpDef OpSTA_DPIndexX = new OpDef(OpSTA) { + Opcode = 0x95, + AddrMode = AddressMode.DPIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpSTX_DPIndexY = new OpDef(OpSTX) { + Opcode = 0x96, + AddrMode = AddressMode.DPIndexY, + CycDef = 4 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0) + }; + public static readonly OpDef OpSTA_DPIndIndexYLong = new OpDef(OpSTA) { + Opcode = 0x97, + AddrMode = AddressMode.DPIndIndexYLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpTYA_Implied = new OpDef(OpTYA) { + Opcode = 0x98, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpSTA_AbsIndexY = new OpDef(OpSTA) { + Opcode = 0x99, + AddrMode = AddressMode.AbsIndexY, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpTXS_Implied = new OpDef(OpTXS) { + Opcode = 0x9a, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpTXY_Implied = new OpDef(OpTXY) { + Opcode = 0x9b, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpSTZ_Abs = new OpDef(OpSTZ) { + Opcode = 0x9c, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpSTA_AbsIndexX = new OpDef(OpSTA) { + Opcode = 0x9d, + AddrMode = AddressMode.AbsIndexX, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpSTZ_AbsIndexX = new OpDef(OpSTZ) { + Opcode = 0x9e, + AddrMode = AddressMode.AbsIndexX, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpSTA_AbsIndexXLong = new OpDef(OpSTA) { + Opcode = 0x9f, + AddrMode = AddressMode.AbsIndexXLong, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpLDY_ImmLongXY = new OpDef(OpLDY) { + Opcode = 0xa0, + AddrMode = AddressMode.ImmLongXY, + StatusFlagUpdater = FlagUpdater_LoadImm, + CycDef = 2 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpLDY_Imm = new OpDef(OpLDY) { + Opcode = 0xa0, + AddrMode = AddressMode.Imm, + StatusFlagUpdater = FlagUpdater_LoadImm, + CycDef = 2 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpLDA_DPIndexXInd = new OpDef(OpLDA) { + Opcode = 0xa1, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpLDX_ImmLongXY = new OpDef(OpLDX) { + Opcode = 0xa2, + AddrMode = AddressMode.ImmLongXY, + StatusFlagUpdater = FlagUpdater_LoadImm, + CycDef = 2 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpLDX_Imm = new OpDef(OpLDX) { + Opcode = 0xa2, + AddrMode = AddressMode.Imm, + StatusFlagUpdater = FlagUpdater_LoadImm, + CycDef = 2 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpLDA_StackRel = new OpDef(OpLDA) { + Opcode = 0xa3, + AddrMode = AddressMode.StackRel, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpLDY_DP = new OpDef(OpLDY) { + Opcode = 0xa4, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0) + }; + public static readonly OpDef OpLDA_DP = new OpDef(OpLDA) { + Opcode = 0xa5, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpLDX_DP = new OpDef(OpLDX) { + Opcode = 0xa6, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0) + }; + public static readonly OpDef OpLDA_DPIndLong = new OpDef(OpLDA) { + Opcode = 0xa7, + AddrMode = AddressMode.DPIndLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpTAY_Implied = new OpDef(OpTAY) { + Opcode = 0xa8, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpLDA_ImmLongA = new OpDef(OpLDA) { // 16-bit CPU + Opcode = 0xa9, + AddrMode = AddressMode.ImmLongA, + StatusFlagUpdater = FlagUpdater_LoadImm, + CycDef = 2 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpLDA_Imm = new OpDef(OpLDA) { // 8-bit CPU + Opcode = 0xa9, + AddrMode = AddressMode.Imm, + StatusFlagUpdater = FlagUpdater_LoadImm, + CycDef = 2 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpTAX_Implied = new OpDef(OpTAX) { + Opcode = 0xaa, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpPLB_StackPull = new OpDef(OpPLB) { + Opcode = 0xab, + AddrMode = AddressMode.StackPull, + CycDef = 4 + }; + public static readonly OpDef OpLDY_Abs = new OpDef(OpLDY) { + Opcode = 0xac, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpLDA_Abs = new OpDef(OpLDA) { + Opcode = 0xad, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpLDX_Abs = new OpDef(OpLDX) { + Opcode = 0xae, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpLDA_AbsLong = new OpDef(OpLDA) { + Opcode = 0xaf, + AddrMode = AddressMode.AbsLong, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpBCS_PCRel = new OpDef(OpBCS) { + Opcode = 0xb0, + AddrMode = AddressMode.PCRel, + CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage) + }; + public static readonly OpDef OpLDA_DPIndIndexY = new OpDef(OpLDA) { + Opcode = 0xb1, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpLDA_DPInd = new OpDef(OpLDA) { + Opcode = 0xb2, + AddrMode = AddressMode.DPInd, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpLDA_StackRelIndIndexY = new OpDef(OpLDA) { + Opcode = 0xb3, + AddrMode = AddressMode.StackRelIndIndexY, + CycDef = 7 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpLDY_DPIndexX = new OpDef(OpLDY) { + Opcode = 0xb4, + AddrMode = AddressMode.DPIndexX, + CycDef = 4 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0) + }; + public static readonly OpDef OpLDA_DPIndexX = new OpDef(OpLDA) { + Opcode = 0xb5, + AddrMode = AddressMode.DPIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpLDX_DPIndexY = new OpDef(OpLDX) { + Opcode = 0xb6, + AddrMode = AddressMode.DPIndexY, + CycDef = 4 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0) + }; + public static readonly OpDef OpLDA_DPIndIndexYLong = new OpDef(OpLDA) { + Opcode = 0xb7, + AddrMode = AddressMode.DPIndIndexYLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpCLV_Implied = new OpDef(OpCLV) { + Opcode = 0xb8, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpLDA_AbsIndexY = new OpDef(OpLDA) { + Opcode = 0xb9, + AddrMode = AddressMode.AbsIndexY, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpTSX_Implied = new OpDef(OpTSX) { + Opcode = 0xba, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpTYX_Implied = new OpDef(OpTYX) { + Opcode = 0xbb, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpLDY_AbsIndexX = new OpDef(OpLDY) { + Opcode = 0xbc, + AddrMode = AddressMode.AbsIndexX, + CycDef = 4 | (int)(CycleMod.OneIfIndexPage | CycleMod.OneIfX0) + }; + public static readonly OpDef OpLDA_AbsIndexX = new OpDef(OpLDA) { + Opcode = 0xbd, + AddrMode = AddressMode.AbsIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpLDX_AbsIndexY = new OpDef(OpLDX) { + Opcode = 0xbe, + AddrMode = AddressMode.AbsIndexY, + CycDef = 4 | (int)(CycleMod.OneIfIndexPage | CycleMod.OneIfX0) + }; + public static readonly OpDef OpLDA_AbsIndexXLong = new OpDef(OpLDA) { + Opcode = 0xbf, + AddrMode = AddressMode.AbsIndexXLong, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpCPY_ImmLongXY = new OpDef(OpCPY) { + Opcode = 0xc0, + AddrMode = AddressMode.ImmLongXY, + CycDef = 2 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpCPY_Imm = new OpDef(OpCPY) { + Opcode = 0xc0, + AddrMode = AddressMode.Imm, + CycDef = 2 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpCMP_DPIndexXInd = new OpDef(OpCMP) { + Opcode = 0xc1, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpREP_Imm = new OpDef(OpREP) { + Opcode = 0xc2, + AddrMode = AddressMode.Imm, + CycDef = 3 + }; + public static readonly OpDef OpCMP_StackRel = new OpDef(OpCMP) { + Opcode = 0xc3, + AddrMode = AddressMode.StackRel, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpCPY_DP = new OpDef(OpCPY) { + Opcode = 0xc4, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0) + }; + public static readonly OpDef OpCMP_DP = new OpDef(OpCMP) { + Opcode = 0xc5, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpDEC_DP = new OpDef(OpDEC) { + Opcode = 0xc6, + AddrMode = AddressMode.DP, + CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpCMP_DPIndLong = new OpDef(OpCMP) { + Opcode = 0xc7, + AddrMode = AddressMode.DPIndLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpINY_Implied = new OpDef(OpINY) { + Opcode = 0xc8, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpCMP_ImmLongA = new OpDef(OpCMP) { + Opcode = 0xc9, + AddrMode = AddressMode.ImmLongA, + CycDef = 2 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpCMP_Imm = new OpDef(OpCMP) { + Opcode = 0xc9, + AddrMode = AddressMode.Imm, + CycDef = 2 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpDEX_Implied = new OpDef(OpDEX) { + Opcode = 0xca, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpWAI_Implied = new OpDef(OpWAI) { + Opcode = 0xcb, + AddrMode = AddressMode.Implied, + CycDef = 3 // 3 to shut down, more to restart + }; + public static readonly OpDef OpCPY_Abs = new OpDef(OpCPY) { + Opcode = 0xcc, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpCMP_Abs = new OpDef(OpCMP) { + Opcode = 0xcd, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpDEC_Abs = new OpDef(OpDEC) { + Opcode = 0xce, + AddrMode = AddressMode.Abs, + CycDef = 6 | (int)(CycleMod.TwoIfM0) + }; + public static readonly OpDef OpCMP_AbsLong = new OpDef(OpCMP) { + Opcode = 0xcf, + AddrMode = AddressMode.AbsLong, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpBNE_PCRel = new OpDef(OpBNE) { + Opcode = 0xd0, + AddrMode = AddressMode.PCRel, + CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage) + }; + public static readonly OpDef OpCMP_DPIndIndexY = new OpDef(OpCMP) { + Opcode = 0xd1, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpCMP_DPInd = new OpDef(OpCMP) { + Opcode = 0xd2, + AddrMode = AddressMode.DPInd, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpCMP_StackRelIndIndexY = new OpDef(OpCMP) { + Opcode = 0xd3, + AddrMode = AddressMode.StackRelIndIndexY, + CycDef = 7 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpPEI_StackDPInd = new OpDef(OpPEI) { + Opcode = 0xd4, + AddrMode = AddressMode.StackDPInd, + CycDef = 6 | (int)(CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpCMP_DPIndexX = new OpDef(OpCMP) { + Opcode = 0xd5, + AddrMode = AddressMode.DPIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpDEC_DPIndexX = new OpDef(OpDEC) { + Opcode = 0xd6, + AddrMode = AddressMode.DPIndexX, + CycDef = 6 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpCMP_DPIndIndexYLong = new OpDef(OpCMP) { + Opcode = 0xd7, + AddrMode = AddressMode.DPIndIndexYLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero) + }; + public static readonly OpDef OpCLD_Implied = new OpDef(OpCLD) { + Opcode = 0xd8, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpCMP_AbsIndexY = new OpDef(OpCMP) { + Opcode = 0xd9, + AddrMode = AddressMode.AbsIndexY, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpPHX_StackPush = new OpDef(OpPHX) { + Opcode = 0xda, + AddrMode = AddressMode.StackPush, + CycDef = 3 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpSTP_Implied = new OpDef(OpSTP) { + Opcode = 0xdb, + AddrMode = AddressMode.Implied, + CycDef = 3 // 3 to shut down, more to reset out + }; + public static readonly OpDef OpJMP_AbsIndLong = new OpDef(OpJML) { + Opcode = 0xdc, + AddrMode = AddressMode.AbsIndLong, + CycDef = 6 + }; + public static readonly OpDef OpCMP_AbsIndexX = new OpDef(OpCMP) { + Opcode = 0xdd, + AddrMode = AddressMode.AbsIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpDEC_AbsIndexX = new OpDef(OpDEC) { + Opcode = 0xde, + AddrMode = AddressMode.AbsIndexX, + CycDef = 7 | (int)(CycleMod.TwoIfM0 | CycleMod.MinusOneIfNoPage) + }; + public static readonly OpDef OpCMP_AbsIndexXLong = new OpDef(OpCMP) { + Opcode = 0xdf, + AddrMode = AddressMode.AbsIndexXLong, + CycDef = 5 | (int)(CycleMod.OneIfM0) + }; + public static readonly OpDef OpCPX_ImmLongXY = new OpDef(OpCPX) { + Opcode = 0xe0, + AddrMode = AddressMode.ImmLongXY, + CycDef = 2 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpCPX_Imm = new OpDef(OpCPX) { + Opcode = 0xe0, + AddrMode = AddressMode.Imm, + CycDef = 2 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpSBC_DPIndexXInd = new OpDef(OpSBC) { + Opcode = 0xe1, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1) + }; + public static readonly OpDef OpSEP_Imm = new OpDef(OpSEP) { + Opcode = 0xe2, + AddrMode = AddressMode.Imm, + CycDef = 3 + }; + public static readonly OpDef OpSBC_StackRel = new OpDef(OpSBC) { + Opcode = 0xe3, + AddrMode = AddressMode.StackRel, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + public static readonly OpDef OpCPX_DP = new OpDef(OpCPX) { + Opcode = 0xe4, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0) + }; + public static readonly OpDef OpSBC_DP = new OpDef(OpSBC) { + Opcode = 0xe5, + AddrMode = AddressMode.DP, + CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1) + }; + public static readonly OpDef OpINC_DP = new OpDef(OpINC) { + Opcode = 0xe6, + AddrMode = AddressMode.DP, + CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpSBC_DPIndLong = new OpDef(OpSBC) { + Opcode = 0xe7, + AddrMode = AddressMode.DPIndLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1) + }; + public static readonly OpDef OpINX_Implied = new OpDef(OpINX) { + Opcode = 0xe8, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpSBC_ImmLongA = new OpDef(OpSBC) { + Opcode = 0xe9, + AddrMode = AddressMode.ImmLongA, + CycDef = 2 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + public static readonly OpDef OpSBC_Imm = new OpDef(OpSBC) { + Opcode = 0xe9, + AddrMode = AddressMode.Imm, + CycDef = 2 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + public static readonly OpDef OpNOP_Implied = new OpDef(OpNOP) { + Opcode = 0xea, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpXBA_Implied = new OpDef(OpXBA) { + Opcode = 0xeb, + AddrMode = AddressMode.Implied, + CycDef = 3 + }; + public static readonly OpDef OpCPX_Abs = new OpDef(OpCPX) { + Opcode = 0xec, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpSBC_Abs = new OpDef(OpSBC) { + Opcode = 0xed, + AddrMode = AddressMode.Abs, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + public static readonly OpDef OpINC_Abs = new OpDef(OpINC) { + Opcode = 0xee, + AddrMode = AddressMode.Abs, + CycDef = 6 | (int)(CycleMod.TwoIfM0) + }; + public static readonly OpDef OpSBC_AbsLong = new OpDef(OpSBC) { + Opcode = 0xef, + AddrMode = AddressMode.AbsLong, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + public static readonly OpDef OpBEQ_PCRel = new OpDef(OpBEQ) { + Opcode = 0xf0, + AddrMode = AddressMode.PCRel, + CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage) + }; + public static readonly OpDef OpSBC_DPIndIndexY = new OpDef(OpSBC) { + Opcode = 0xf1, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | + CycleMod.OneIfIndexPage | CycleMod.OneIfD1) + }; + public static readonly OpDef OpSBC_DPInd = new OpDef(OpSBC) { + Opcode = 0xf2, + AddrMode = AddressMode.DPInd, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1) + }; + public static readonly OpDef OpSBC_StackRelIndIndexY = new OpDef(OpSBC) { + Opcode = 0xf3, + AddrMode = AddressMode.StackRelIndIndexY, + CycDef = 7 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + public static readonly OpDef OpPEA_StackAbs = new OpDef(OpPEA) { + Opcode = 0xf4, + AddrMode = AddressMode.StackAbs, + CycDef = 5 + }; + public static readonly OpDef OpSBC_DPIndexX = new OpDef(OpSBC) { + Opcode = 0xf5, + AddrMode = AddressMode.DPIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1) + }; + public static readonly OpDef OpINC_DPIndexX = new OpDef(OpINC) { + Opcode = 0xf6, + AddrMode = AddressMode.DPIndexX, + CycDef = 6 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0) + }; + public static readonly OpDef OpSBC_DPIndIndexYLong = new OpDef(OpSBC) { + Opcode = 0xf7, + AddrMode = AddressMode.DPIndIndexYLong, + CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1) + }; + public static readonly OpDef OpSED_Implied = new OpDef(OpSED) { + Opcode = 0xf8, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpSBC_AbsIndexY = new OpDef(OpSBC) { + Opcode = 0xf9, + AddrMode = AddressMode.AbsIndexY, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage | CycleMod.OneIfD1) + }; + public static readonly OpDef OpPLX_StackPull = new OpDef(OpPLX) { + Opcode = 0xfa, + AddrMode = AddressMode.StackPull, + CycDef = 4 | (int)(CycleMod.OneIfX0) + }; + public static readonly OpDef OpXCE_Implied = new OpDef(OpXCE) { + Opcode = 0xfb, + AddrMode = AddressMode.Implied, + CycDef = 2 + }; + public static readonly OpDef OpJSR_AbsIndexXInd = new OpDef(OpJSR) { + Opcode = 0xfc, + AddrMode = AddressMode.AbsIndexXInd, + CycDef = 8 + }; + public static readonly OpDef OpSBC_AbsIndexX = new OpDef(OpSBC) { + Opcode = 0xfd, + AddrMode = AddressMode.AbsIndexX, + CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage | CycleMod.OneIfD1) + }; + public static readonly OpDef OpINC_AbsIndexX = new OpDef(OpINC) { + Opcode = 0xfe, + AddrMode = AddressMode.AbsIndexX, + CycDef = 7 | (int)(CycleMod.TwoIfM0 | CycleMod.MinusOneIfNoPage) + }; + public static readonly OpDef OpSBC_AbsIndexXLong = new OpDef(OpSBC) { + Opcode = 0xff, + AddrMode = AddressMode.AbsIndexXLong, + CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1) + }; + + #endregion 65816 Instructions + + #region Undocumented 6502 + + // ====================================================================================== + // Undocumented 6502 instructions. + // + // There are 151 defined opcodes. The rest officially have undefined behavior. In + // most cases it's pretty stable, in others the behavior can differ between CPU + // variants. + // + // There is no generally agreed-upon set of mnemonics for these instructions. The + // mnemonics "XAS" and "AXS" sometimes mean one thing and sometimes another. I've + // chosen a set that seems reasonable. If a consensus is reached, the mnemonics + // are easy enough to change throughout (yay Visual Studio refactoring). + // + // References: + // http://nesdev.com/undocumented_opcodes.txt + // http://visual6502.org/wiki/index.php?title=6502_all_256_Opcodes + // http://www.ffd2.com/fridge/docs/6502-NMOS.extra.opcodes + // + + private static OpDef OpANC = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.ANC, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, + StatusFlagUpdater = FlagUpdater_NZC + }; + private static OpDef OpANE = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.ANE, + Effect = FlowEffect.Cont + }; + private static OpDef OpARR = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.ARR, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NVZC, + StatusFlagUpdater = FlagUpdater_NVZC + }; + private static OpDef OpASR = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.ASR, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, + StatusFlagUpdater = FlagUpdater_NZC + }; + private static OpDef OpDCP = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.DCP, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_C, + StatusFlagUpdater = FlagUpdater_C + }; + private static OpDef OpDOP = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.DOP, + Effect = FlowEffect.Cont + }; + private static OpDef OpHLT = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.HLT, + Effect = FlowEffect.NoCont + }; + private static OpDef OpISB = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.ISB, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NVZC, + StatusFlagUpdater = FlagUpdater_NVZC + }; + private static OpDef OpLAE = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.LAE, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpLAX = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.LAX, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpLXA = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.LXA, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpRLA = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.RLA, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, + StatusFlagUpdater = FlagUpdater_NZC + }; + private static OpDef OpRRA = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.RRA, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NVZC, + StatusFlagUpdater = FlagUpdater_NVZC + }; + private static OpDef OpSAX = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.SAX, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZ, + StatusFlagUpdater = FlagUpdater_NZ + }; + private static OpDef OpSBX = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.SBX, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, + StatusFlagUpdater = FlagUpdater_NZC + }; + private static OpDef OpSHA = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.SHA, + Effect = FlowEffect.Cont + }; + private static OpDef OpSHS = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.SHS, + Effect = FlowEffect.Cont + }; + private static OpDef OpSHX = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.SHX, + Effect = FlowEffect.Cont + }; + private static OpDef OpSHY = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.SHY, + Effect = FlowEffect.Cont + }; + private static OpDef OpSLO = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.SLO, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, + StatusFlagUpdater = FlagUpdater_NZC + }; + private static OpDef OpSRE = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.SRE, + Effect = FlowEffect.Cont, + FlagsAffected = FlagsAffected_NZC, + StatusFlagUpdater = FlagUpdater_NZC + }; + private static OpDef OpTOP = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.TOP, + Effect = FlowEffect.Cont + }; + public static readonly OpDef OpSLO_DPIndexXInd = new OpDef(OpSLO) { + Opcode = 0x03, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 8 + }; + public static readonly OpDef OpSLO_DP = new OpDef(OpSLO) { + Opcode = 0x07, + AddrMode = AddressMode.DP, + CycDef = 5 + }; + public static readonly OpDef OpSLO_Absolute = new OpDef(OpSLO) { + Opcode = 0x0f, + AddrMode = AddressMode.Abs, + CycDef = 6 + }; + public static readonly OpDef OpSLO_DPIndIndexY = new OpDef(OpSLO) { + Opcode = 0x13, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 8 + }; + public static readonly OpDef OpSLO_DPIndexX = new OpDef(OpSLO) { + Opcode = 0x17, + AddrMode = AddressMode.DPIndexX, + CycDef = 6 + }; + public static readonly OpDef OpSLO_AbsIndexY = new OpDef(OpSLO) { + Opcode = 0x1b, + AddrMode = AddressMode.AbsIndexY, + CycDef = 7 + }; + public static readonly OpDef OpSLO_AbsIndexX = new OpDef(OpSLO) { + Opcode = 0x1f, + AddrMode = AddressMode.AbsIndexX, + CycDef = 7 + }; + public static readonly OpDef OpRLA_DPIndexXInd = new OpDef(OpRLA) { + Opcode = 0x23, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 8 + }; + public static readonly OpDef OpRLA_DP = new OpDef(OpRLA) { + Opcode = 0x27, + AddrMode = AddressMode.DP, + CycDef = 5 + }; + public static readonly OpDef OpRLA_Absolute = new OpDef(OpRLA) { + Opcode = 0x2f, + AddrMode = AddressMode.Abs, + CycDef = 6 + }; + public static readonly OpDef OpRLA_DPIndIndexY = new OpDef(OpRLA) { + Opcode = 0x33, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 8 + }; + public static readonly OpDef OpRLA_DPIndexX = new OpDef(OpRLA) { + Opcode = 0x37, + AddrMode = AddressMode.DPIndexX, + CycDef = 6 + }; + public static readonly OpDef OpRLA_AbsIndexY = new OpDef(OpRLA) { + Opcode = 0x3b, + AddrMode = AddressMode.AbsIndexY, + CycDef = 7 + }; + public static readonly OpDef OpRLA_AbsIndexX = new OpDef(OpRLA) { + Opcode = 0x3f, + AddrMode = AddressMode.AbsIndexX, + CycDef = 7 + }; + public static readonly OpDef OpSRE_DPIndexXInd = new OpDef(OpSRE) { + Opcode = 0x43, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 8 + }; + public static readonly OpDef OpSRE_DP = new OpDef(OpSRE) { + Opcode = 0x47, + AddrMode = AddressMode.DP, + CycDef = 5 + }; + public static readonly OpDef OpSRE_Absolute = new OpDef(OpSRE) { + Opcode = 0x4f, + AddrMode = AddressMode.Abs, + CycDef = 6 + }; + public static readonly OpDef OpSRE_DPIndIndexY = new OpDef(OpSRE) { + Opcode = 0x53, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 8 + }; + public static readonly OpDef OpSRE_DPIndexX = new OpDef(OpSRE) { + Opcode = 0x57, + AddrMode = AddressMode.DPIndexX, + CycDef = 6 + }; + public static readonly OpDef OpSRE_AbsIndexY = new OpDef(OpSRE) { + Opcode = 0x5b, + AddrMode = AddressMode.AbsIndexY, + CycDef = 7 + }; + public static readonly OpDef OpSRE_AbsIndexX = new OpDef(OpSRE) { + Opcode = 0x5f, + AddrMode = AddressMode.AbsIndexX, + CycDef = 7 + }; + public static readonly OpDef OpRRA_DPIndexXInd = new OpDef(OpRRA) { + Opcode = 0x63, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 8 + }; + public static readonly OpDef OpRRA_DP = new OpDef(OpRRA) { + Opcode = 0x67, + AddrMode = AddressMode.DP, + CycDef = 5 + }; + public static readonly OpDef OpRRA_Absolute = new OpDef(OpRRA) { + Opcode = 0x6f, + AddrMode = AddressMode.Abs, + CycDef = 6 + }; + public static readonly OpDef OpRRA_DPIndIndexY = new OpDef(OpRRA) { + Opcode = 0x73, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 8 + }; + public static readonly OpDef OpRRA_DPIndexX = new OpDef(OpRRA) { + Opcode = 0x77, + AddrMode = AddressMode.DPIndexX, + CycDef = 6 + }; + public static readonly OpDef OpRRA_AbsIndexY = new OpDef(OpRRA) { + Opcode = 0x7b, + AddrMode = AddressMode.AbsIndexY, + CycDef = 7 + }; + public static readonly OpDef OpRRA_AbsIndexX = new OpDef(OpRRA) { + Opcode = 0x7f, + AddrMode = AddressMode.AbsIndexX, + CycDef = 7 + }; + public static readonly OpDef OpSAX_DPIndexXInd = new OpDef(OpSAX) { + Opcode = 0x83, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 6 + }; + public static readonly OpDef OpSAX_DP = new OpDef(OpSAX) { + Opcode = 0x87, + AddrMode = AddressMode.DP, + CycDef = 3 + }; + public static readonly OpDef OpSAX_Absolute = new OpDef(OpSAX) { + Opcode = 0x8f, + AddrMode = AddressMode.Abs, + CycDef = 4 + }; + public static readonly OpDef OpSAX_DPIndexY = new OpDef(OpSAX) { + Opcode = 0x97, + AddrMode = AddressMode.DPIndexY, + CycDef = 4 + }; + public static readonly OpDef OpLAX_DPIndexXInd = new OpDef(OpLAX) { + Opcode = 0xa3, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 6 + }; + public static readonly OpDef OpLAX_DP = new OpDef(OpLAX) { + Opcode = 0xa7, + AddrMode = AddressMode.DP, + CycDef = 3 + }; + public static readonly OpDef OpLAX_Absolute = new OpDef(OpLAX) { + Opcode = 0xaf, + AddrMode = AddressMode.Abs, + CycDef = 4 + }; + public static readonly OpDef OpLAX_DPIndIndexY = new OpDef(OpLAX) { + Opcode = 0xb3, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 5 | (int)(CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpLAX_DPIndexY = new OpDef(OpLAX) { + Opcode = 0xb7, + AddrMode = AddressMode.DPIndexY, + CycDef = 4 + }; + public static readonly OpDef OpLAX_AbsIndexY = new OpDef(OpLAX) { + Opcode = 0xbf, + AddrMode = AddressMode.AbsIndexY, + CycDef = 4 | (int)(CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpDCP_DPIndexXInd = new OpDef(OpDCP) { + Opcode = 0xc3, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 8 + }; + public static readonly OpDef OpDCP_DP = new OpDef(OpDCP) { + Opcode = 0xc7, + AddrMode = AddressMode.DP, + CycDef = 5 + }; + public static readonly OpDef OpDCP_Abs = new OpDef(OpDCP) { + Opcode = 0xcf, + AddrMode = AddressMode.Abs, + CycDef = 6 + }; + public static readonly OpDef OpDCP_DPIndIndexY = new OpDef(OpDCP) { + Opcode = 0xd3, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 8 + }; + public static readonly OpDef OpDCP_DPIndexX = new OpDef(OpDCP) { + Opcode = 0xd7, + AddrMode = AddressMode.DPIndexX, + CycDef = 6 + }; + public static readonly OpDef OpDCP_AbsIndexY = new OpDef(OpDCP) { + Opcode = 0xdb, + AddrMode = AddressMode.AbsIndexY, + CycDef = 7 + }; + public static readonly OpDef OpDCP_AbsIndexX = new OpDef(OpDCP) { + Opcode = 0xdf, + AddrMode = AddressMode.AbsIndexX, + CycDef = 7 + }; + public static readonly OpDef OpISB_DPIndexXInd = new OpDef(OpISB) { + Opcode = 0xe3, + AddrMode = AddressMode.DPIndexXInd, + CycDef = 8 + }; + public static readonly OpDef OpISB_DP = new OpDef(OpISB) { + Opcode = 0xe7, + AddrMode = AddressMode.DP, + CycDef = 5 + }; + public static readonly OpDef OpISB_Abs = new OpDef(OpISB) { + Opcode = 0xef, + AddrMode = AddressMode.Abs, + CycDef = 6 + }; + public static readonly OpDef OpISB_DPIndIndexY = new OpDef(OpISB) { + Opcode = 0xf3, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 8 + }; + public static readonly OpDef OpISB_DPIndexX = new OpDef(OpISB) { + Opcode = 0xf7, + AddrMode = AddressMode.DPIndexX, + CycDef = 6 + }; + public static readonly OpDef OpISB_AbsIndexY = new OpDef(OpISB) { + Opcode = 0xfb, + AddrMode = AddressMode.AbsIndexY, + CycDef = 7 + }; + public static readonly OpDef OpISB_AbsIndexX = new OpDef(OpISB) { + Opcode = 0xff, + AddrMode = AddressMode.AbsIndexX, + CycDef = 7 + }; + public static readonly OpDef OpASR_Imm = new OpDef(OpASR) { + Opcode = 0x4b, + AddrMode = AddressMode.Imm, + CycDef = 2 + }; + public static readonly OpDef OpARR_Imm = new OpDef(OpARR) { + Opcode = 0x6b, + AddrMode = AddressMode.Imm, + CycDef = 2 + }; + public static readonly OpDef OpANE_Imm = new OpDef(OpANE) { + Opcode = 0x8b, + AddrMode = AddressMode.Imm, + CycDef = 2 + }; + public static readonly OpDef OpLXA_Imm = new OpDef(OpLXA) { + Opcode = 0xab, + AddrMode = AddressMode.Imm, + CycDef = 2 + }; + public static readonly OpDef OpSBX_Imm = new OpDef(OpSBX) { + Opcode = 0xcb, + AddrMode = AddressMode.Imm, + CycDef = 2 + }; + public static readonly OpDef OpDOP_Imm = new OpDef(OpDOP) { + // multiple opcodes + AddrMode = AddressMode.Imm, + CycDef = 2 + }; + public static readonly OpDef OpDOP_DP = new OpDef(OpDOP) { + // multiple opcodes + AddrMode = AddressMode.DP, + CycDef = 3 + }; + public static readonly OpDef OpDOP_DPIndexX = new OpDef(OpDOP) { + // multiple opcodes + AddrMode = AddressMode.DPIndexX, + CycDef = 4 + }; + public static readonly OpDef OpTOP_Abs = new OpDef(OpTOP) { + Opcode = 0x0c, + AddrMode = AddressMode.Abs, + CycDef = 4 + }; + public static readonly OpDef OpTOP_AbsIndeX = new OpDef(OpTOP) { + // multiple opcodes + AddrMode = AddressMode.AbsIndexX, + CycDef = 4 | (int)(CycleMod.OneIfIndexPage) + }; + public static readonly OpDef OpHLT_Implied = new OpDef(OpHLT) { + // multiple opcodes + AddrMode = AddressMode.Implied, + CycDef = 1 + }; + public static readonly OpDef OpSHS_AbsIndexY = new OpDef(OpSHS) { + Opcode = 0x9b, + AddrMode = AddressMode.AbsIndexY, + CycDef = 5 + }; + public static readonly OpDef OpSHY_AbsIndexX = new OpDef(OpSHY) { + Opcode = 0x9c, + AddrMode = AddressMode.AbsIndexX, + CycDef = 5 + }; + public static readonly OpDef OpSHX_AbsIndexY = new OpDef(OpSHX) { + Opcode = 0x9e, + AddrMode = AddressMode.AbsIndexY, + CycDef = 5 + }; + public static readonly OpDef OpSHA_DPIndIndexY = new OpDef(OpSHA) { + Opcode = 0x93, + AddrMode = AddressMode.DPIndIndexY, + CycDef = 6 + }; + public static readonly OpDef OpSHA_AbsIndexY = new OpDef(OpSHA) { + Opcode = 0x9f, + AddrMode = AddressMode.AbsIndexY, + CycDef = 5 + }; + public static readonly OpDef OpANC_Imm = new OpDef(OpANC) { + // multiple opcodes + AddrMode = AddressMode.Imm, + CycDef = 2 + }; + public static readonly OpDef OpLAE_AbsIndexY = new OpDef(OpLAE) { + Opcode = 0xbb, + AddrMode = AddressMode.AbsIndexY, + CycDef = 4 | (int)(CycleMod.OneIfIndexPage) + }; + + #endregion Undocumented 6502 + + #region Undocumented 65C02 + + // ====================================================================================== + // Undocumented 65C02 instructions. + // + // The 65C02 declared all undefined opcodes to be NOPs, but some of them can have + // side effects. + // + // References: + // http://laughtonelectronics.com/Arcana/KimKlone/Kimklone_opcode_mapping.html + // + + // Most undocumented instructions are a single-byte, single-cycle NOP. I don't know + // if these are "undocumented" in the strictest sense of the word, but we'll treat + // them that way for disassembly purposes. + public static readonly OpDef OpNOP_65C02 = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.NOP, + Effect = FlowEffect.Cont, + AddrMode = AddressMode.Implied, + CycDef = 1 + }; + + // 65C02 undocumented "Load and Discard" instruction. These cause bus traffic. + private static OpDef OpLDD = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.LDD, + Effect = FlowEffect.Cont, + AddrMode = AddressMode.Implied + }; + public static readonly OpDef OpLDD_Absolute = new OpDef(OpLDD) { + // multiple opcodes + AddrMode = AddressMode.Abs, + CycDef = 4 + }; + public static readonly OpDef OpLDD_DP = new OpDef(OpLDD) { + // multiple opcodes + AddrMode = AddressMode.DP, + CycDef = 3 + }; + public static readonly OpDef OpLDD_DPIndexX = new OpDef(OpLDD) { + // multiple opcodes + AddrMode = AddressMode.DPIndexX, + CycDef = 4 + }; + public static readonly OpDef OpLDD_Imm = new OpDef(OpLDD) { + // multiple opcodes + AddrMode = AddressMode.Imm, + CycDef = 2 + }; + public static readonly OpDef OpLDD_Weird = new OpDef(OpLDD) { + // multiple opcodes + AddrMode = AddressMode.Abs, // not really, but has the right number of bytes + CycDef = 8 + }; + + #endregion Undocumented + + + /// + /// Generates one of the multiply-defined opcodes from a prototype. This is + /// particularly useful for undocumented opcodes that are repeated several times. In + /// a couple of cases (SBC, some NOPs), the undocumented opcode is just another + /// reference to an existing instruction. + /// + /// Instruction opcode. + /// Instruction prototype. + /// Newly-created OpDef. + public static OpDef GenerateUndoc(byte opcode, OpDef proto) { + return new OpDef(proto) { Opcode = opcode, IsUndocumented = true }; + } + + public override string ToString() { + return Opcode.ToString("x2") + "/" + Mnemonic + " " + AddrMode; + } + } +} diff --git a/Asm65/OpDescription.cs b/Asm65/OpDescription.cs new file mode 100644 index 0000000..d283154 --- /dev/null +++ b/Asm65/OpDescription.cs @@ -0,0 +1,738 @@ +/* + * Copyright 2018 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.Text; + +namespace Asm65 { + /// + /// Human-readable text describing instructions. + /// + /// The expectation is that the long description will include information about all + /// address modes and any differences in behavior between CPUs. So there will be one + /// entry per instruction mnemonic, and one global table for all CPUs, rather than one + /// entry per opcode and one instance per CpuDef. + /// + /// There may, however, be different instances for different Cultures. Also, the 65816 + /// traditionally splits JSR/JSL and JMP/JML, so in that case there will be two entries + /// for the same instruction. + /// + public class OpDescription { + private Dictionary mShortDescriptions; + + private Dictionary mLongDescriptions; + + private Dictionary mAddressModeDescriptions; + + private Dictionary mCycleModDescriptions; + + private OpDescription(Dictionary sd, Dictionary ld, + Dictionary am, Dictionary cm) { + mShortDescriptions = sd; + mLongDescriptions = ld; + mAddressModeDescriptions = am; + mCycleModDescriptions = cm; + } + + /// + /// Returns an OpDescription instance for the requested region. + /// + /// TBD + public static OpDescription GetOpDescription(string region) { + // ignoring region for now + return new OpDescription(sShort_enUS, sLong_enUS, sAddrMode_enUS, sCycleMod_enUS); + } + + /// + /// Short description of instruction, e.g. "Load Accumulator". + /// + /// Instruction mnemonic. + /// Short description string, or empty string if not found. + public string GetShortDescription(string mnemonic) { + if (mShortDescriptions.TryGetValue(mnemonic, out string desc)) { + return desc; + } else { + return string.Empty; + } + } + + /// + /// Long description of instruction. May span multiple lines, with embedded CRLF at + /// paragraph breaks. + /// + /// Instruction mnemonic. + /// Long description string, or empty string if not found. + public string GetLongDescription(string mnemonic) { + if (mLongDescriptions.TryGetValue(mnemonic, out string desc)) { + return desc; + } else { + return string.Empty; + } + } + + /// + /// Address mode short description. + /// + /// Address mode to look up. + /// Description string, or an empty string for instructions + /// with implied address modes. + public string GetAddressModeDescription(OpDef.AddressMode addrMode) { + if (mAddressModeDescriptions.TryGetValue(addrMode, out string desc)) { + return desc; + } else { + return string.Empty; + } + } + + /// + /// Cycle modifier description. + /// + /// A single-bit item from the CycleMod enum. + /// Description string, or question marks if not found. + public string GetCycleModDescription(OpDef.CycleMod modBit) { + if (mCycleModDescriptions.TryGetValue(modBit, out string desc)) { + return desc; + } else { + return "???"; + } + } + + /// + /// Short descriptions, USA English. + /// + /// Text is adapted from instruction summaries in Eyes & Lichty, which are slightly + /// shorter than those in the CPU data sheet. + /// + private static Dictionary sShort_enUS = new Dictionary() { + { OpName.ADC, "Add With Carry" }, + { OpName.AND, "AND Accumulator With Memory" }, + { OpName.ASL, "Shift Memory or Accumulator Left" }, + { OpName.BCC, "Branch If Carry Clear" }, + { OpName.BCS, "Branch If Carry Set" }, + { OpName.BEQ, "Branch If Equal" }, + { OpName.BIT, "Test Memory Against Accumulator" }, + { OpName.BMI, "Branch If Minus" }, + { OpName.BNE, "Branch If Not Equal" }, + { OpName.BPL, "Branch If Plus" }, + { OpName.BRA, "Branch Always" }, + { OpName.BRK, "Software Break" }, + { OpName.BRL, "Branch Always Long" }, + { OpName.BVC, "Branch If Overflow Clear" }, + { OpName.BVS, "Branch If Overflow Set" }, + { OpName.CLC, "Clear Carry Flag" }, + { OpName.CLD, "Clear Decimal Flag" }, + { OpName.CLI, "Clear Interrupt Disable Flag" }, + { OpName.CLV, "Clear Overflow Flag" }, + { OpName.CMP, "Compare Accumulator With Memory" }, + { OpName.COP, "Co-Processor" }, + { OpName.CPX, "Compare Index X With Memory" }, + { OpName.CPY, "Compare Index Y With Memory" }, + { OpName.DEC, "Decrement Accumulator" }, + { OpName.DEX, "Decrement Index X" }, + { OpName.DEY, "Decrement Index Y" }, + { OpName.EOR, "XOR Accumulator With Memory" }, + { OpName.INC, "Increment Accumulator" }, + { OpName.INX, "Increment Index X" }, + { OpName.INY, "Increment Index Y" }, + { OpName.JMP, "Jump" }, + { OpName.JSL, "Jump to Subroutine Long" }, + { OpName.JSR, "Jump to Subroutine" }, + { OpName.LDA, "Load Accumulator from Memory" }, + { OpName.LDX, "Load Index X from Memory" }, + { OpName.LDY, "Load Index Y from Memory" }, + { OpName.LSR, "Logical Shift Memory or Accumulator Right" }, + { OpName.MVN, "Block Move Next" }, + { OpName.MVP, "Block Move Previous" }, + { OpName.NOP, "No Operation" }, + { OpName.ORA, "OR Accumulator With Memory" }, + { OpName.PEA, "Push Effective Absolute Address" }, + { OpName.PEI, "Push Effective Indirect Address" }, + { OpName.PER, "Push Effective Relative Indirect Address" }, + { OpName.PHA, "Push Accumulator" }, + { OpName.PHB, "Push Data Bank Register" }, + { OpName.PHD, "Push Direct Page Register" }, + { OpName.PHK, "Push Program Bank Register" }, + { OpName.PHP, "Push Processor Status Register" }, + { OpName.PHX, "Push Index Register X" }, + { OpName.PHY, "Push Index Register Y" }, + { OpName.PLA, "Pull Accumulator" }, + { OpName.PLB, "Pull Data Bank Register" }, + { OpName.PLD, "Pull Direct Page Register" }, + { OpName.PLP, "Pull Processor Status Register" }, + { OpName.PLX, "Pull Index Register X" }, + { OpName.PLY, "Pull Index Register Y" }, + { OpName.REP, "Reset Status Bits" }, + { OpName.ROL, "Rotate Memory or Accumulator Left" }, + { OpName.ROR, "Rotate Memory or Accumulator Right" }, + { OpName.RTI, "Return from Interrupt" }, + { OpName.RTL, "Return from Subroutine Long" }, + { OpName.RTS, "Return from Subroutine" }, + { OpName.SBC, "Subtract With Borrow" }, + { OpName.SEC, "Set Carry Flag" }, + { OpName.SED, "Set Decimal Flag" }, + { OpName.SEI, "Set Interrupt Disable Flag" }, + { OpName.SEP, "Set Status Bits" }, + { OpName.STA, "Store Accumulator to Memory" }, + { OpName.STP, "Stop Processor" }, + { OpName.STX, "Store Index X to Memory" }, + { OpName.STY, "Store Index Y to Memory" }, + { OpName.STZ, "Store Zero to Memory" }, + { OpName.TAX, "Transfer Accumulator to Index X" }, + { OpName.TAY, "Transfer Accumulator to Index Y" }, + { OpName.TCD, "Transfer 16-Bit Accumulator to Direct Page Register" }, + { OpName.TCS, "Transfer Accumulator to Stack Pointer" }, + { OpName.TDC, "Transfer Direct Page Register to 16-Bit Accumulator" }, + { OpName.TRB, "Test and Reset Memory Bits" }, + { OpName.TSB, "Test and Set Memory Bits" }, + { OpName.TSC, "Transfer Stack Pointer to 16-Bit Accumulator" }, + { OpName.TSX, "Transfer Stack Pointer to Index X" }, + { OpName.TXA, "Transfer Index X to Accumulator" }, + { OpName.TXS, "Transfer Index X to Stack Pointer" }, + { OpName.TXY, "Transfer Index X to Index Y" }, + { OpName.TYA, "Transfer Index Y to Accumulator" }, + { OpName.TYX, "Transfer Index Y to Index X" }, + { OpName.WAI, "Wait for Interrupt" }, + { OpName.WDM, "Future Expansion" }, + { OpName.XBA, "Exchange Accumulator B and A" }, + { OpName.XCE, "Exchange Carry and Emulation Bits" }, + + // MOS 6502 undocumented ops + { OpName.ANC, "AND Accumulator With Value and Set Carry" }, + { OpName.ANE, "Transfer Index X to Accumulator and AND" }, + { OpName.ARR, "AND and Rotate Right" }, + { OpName.ASR, "AND and Shift Right" }, + { OpName.DCP, "Decrement and Compare" }, + { OpName.DOP, "Double-Byte NOP" }, + { OpName.HLT, "Halt CPU" }, + { OpName.ISB, "Increment and Subtract" }, + { OpName.LAE, "Load Acc, X, and Stack Pointer with Memory AND Stack Pointer" }, + { OpName.LAX, "Load Accumulator and Index X" }, + { OpName.LXA, "OR, AND, and Transfer to X" }, + { OpName.RLA, "Rotate Left and AND" }, + { OpName.RRA, "Rotate Right and Add" }, + { OpName.SAX, "Store Accumulator AND Index X" }, // AXS + { OpName.SBX, "AND Acc With Index X, Subtract, and Store in X" }, // SAX + { OpName.SHA, "AND Acc With Index X and High Byte, and Store" }, // AXA + { OpName.SHS, "AND Acc with Index X, Transfer to Stack, AND High Byte" }, // TAS + { OpName.SHX, "AND Acc With Index X and High Byte, and Store" }, // XAS + { OpName.SHY, "AND Acc With Index Y and High Byte, and Store" }, // SAY + { OpName.SLO, "Shift Left and OR" }, + { OpName.SRE, "Shift right and EOR" }, + { OpName.TOP, "Triple-Byte NOP" }, + + // WDC 65C02 undocumented + { OpName.LDD, "Load and Discard" }, + }; + + /// + /// Long descriptions, USA English. + /// + private static Dictionary sLong_enUS = new Dictionary() { + { OpName.ADC, + "Adds the accumulator and a value in memory, storing the result in the " + + "accumulator. Adds one if the carry is set." + }, + { OpName.AND, + "Performs a bitwise AND of the accumulator with a value in memory, storing " + + "the result in the accumulator." + }, + { OpName.ASL, + "Shifts memory or the accumulator one bit left. The low bit is set to zero, " + + "and the carry flag receives the high bit." + }, + { OpName.BCC, + "Branches to a relative address if the processor carry flag (C) is zero. " + + "Sometimes referred to as Branch If Less Than, or BLT." + }, + { OpName.BCS, + "Branches to a relative address if the processor carry flag (C) is one. " + + "Sometimes referred to as Branch If Greater Than or Equal, or BGE." + }, + { OpName.BEQ, + "Branches to a relative address if the processor zero flag (Z) is one." + }, + { OpName.BIT, + "Sets processor flags based on the result of two operations. The N and V flags " + + "are set according to bits 7 and 6, and the Z flag is set based on an AND of " + + "the accumulator and memory. However, when used with immediate addressing, " + + "the N and V flags are not affected." + }, + { OpName.BMI, + "Branches to a relative address if the processor negative flag (N) is one." + }, + { OpName.BNE, + "Branches to a relative address if the processor zero flag (Z) is zero." + }, + { OpName.BPL, + "Branches to a relative address if the processor negative flag (N) is zero." + }, + { OpName.BRA, + "Branches to a relative address." + }, + { OpName.BRK, + "Pushes state onto the stack, and jumps to the software break vector at " + + "$fffe-ffff. While this is technically a single-byte instruction, the " + + "program counter pushed onto the stack is incremented by two." + }, + { OpName.BRL, + "Branches to a long relative address." + }, + { OpName.BVC, + "Branches to a relative address if the processor overflow flag (V) is zero." + }, + { OpName.BVS, + "Branches to a relative address if the processor overflow flag (V) is one." + }, + { OpName.CLC, + "Sets the processor carry flag (C) to zero." + }, + { OpName.CLD, + "Sets the processor decimal flag (D) to zero." + }, + { OpName.CLI, + "Sets the processor interrupt disable flag (I) to zero." + }, + { OpName.CLV, + "Sets the processor overflow flag (V) to zero." + }, + { OpName.CMP, + "Subtracts the value specified by the operand from the contents of the " + + "accumulator. Sets the carry, zero, and negative flags, but does not alter " + + "memory or the accumulator." + }, + { OpName.COP, + "Pushes state onto the stack, and jumps to the software interrupt vector at " + + "$fff4-fff5." + }, + { OpName.CPX, + "Subtracts the value specified by the operand from the contents of the " + + "X register. Sets the carry, zero, and negative flags, but does not alter " + + "memory or the X register." + }, + { OpName.CPY, + "Subtracts the value specified by the operand from the contents of the " + + "Y register. Sets the carry, zero, and negative flags, but does not alter " + + "memory or the Y register." + }, + { OpName.DEC, + "Decrements the contents of the location specified by the operand by one." + }, + { OpName.DEX, + "Decrements the X register by one." + }, + { OpName.DEY, + "Decrements the Y register by one." + }, + { OpName.EOR, + "Performs a bitwise EOR of the accumulator with a value in memory, storing " + + "the result in the accumulator." + }, + { OpName.INC, + "Increments the contents of the location specified by the operand by one." + }, + { OpName.INX, + "Increments the X register by one." + }, + { OpName.INY, + "Increments the Y register by one." + }, + { OpName.JML, + "Branches to a long absolute address." + }, + { OpName.JMP, + "Branches to an absolute address." + }, + { OpName.JSL, + "Branches to a long absolute address after pushing the current address onto " + + "the stack. The value pushed is the address of the last operand byte." + }, + { OpName.JSR, + "Branches to an absolute address after pushing the current address onto " + + "the stack. The value pushed is the address of the last operand byte." + }, + { OpName.LDA, + "Loads the accumulator from memory." + }, + { OpName.LDX, + "Loads the X register from memory." + }, + { OpName.LDY, + "Loads the Y register from memory." + }, + { OpName.LSR, + "Shifts memory or the accumulator one bit right. The high bit is set to zero, " + + "and the carry flag receives the low bit." + }, + { OpName.MVN, + "Moves a block of memory, starting from a low address and incrementing. " + + "The source and destination addresses are in the X and Y registers, " + + "respectively. The accumulator holds the number of bytes to move minus 1, " + + "and the source and destination banks are specified by the operands." + }, + { OpName.MVP, + "Moves a block of memory, starting from a high address and decrementing. " + + "The source and destination addresses are in the X and Y registers, " + + "respectively. The accumulator holds the number of bytes to move minus 1, " + + "and the source and destination banks are specified by the operands." + }, + { OpName.NOP, + "No operation." + }, + { OpName.ORA, + "Performs a bitwise OR of the accumulator with a value in memory, storing " + + "the result in the accumulator." + }, + { OpName.PEA, + "Pushes the 16-bit operand onto the stack. This always pushes two bytes, " + + "regardless of the M/X processor flags." + }, + { OpName.PEI, + "Pushes a 16-bit value from the direct page onto the stack." + }, + { OpName.PER, + "Converts a relative offset to an absolute address, and pushes it onto the stack." + }, + { OpName.PHA, + "Pushes the accumulator onto the stack." + }, + { OpName.PHB, + "Pushes the data bank register onto the stack." + }, + { OpName.PHD, + "Pushes the direct page register onto the stack." + }, + { OpName.PHK, + "Pushes the program bank register onto the stack." + }, + { OpName.PHP, + "Pushes the processor status register onto the stack." + }, + { OpName.PHX, + "Pushes the X register onto the stack." + }, + { OpName.PHY, + "Pushes the Y register onto the stack." + }, + { OpName.PLA, + "Pulls the accumulator off of the stack." + }, + { OpName.PLB, + "Pulls the data bank register off of the stack." + }, + { OpName.PLD, + "Pulls the direct page register off of the stack." + }, + { OpName.PLP, + "Pulls the processor status register off of the stack." + }, + { OpName.PLX, + "Pulls the X register off of the stack." + }, + { OpName.PLY, + "Pulls the Y register off of the stack." + }, + { OpName.REP, + "Sets specific bits in the processor status register to zero." + }, + { OpName.ROL, + "Rotates memory or the accumulator one bit left. The low bit is set to the " + + "carry flag, and the carry flag receives the high bit." + }, + { OpName.ROR, + "Rotates memory or the accumulator one bit right. The high bit is set to the " + + "carry flag, and the carry flag receives the low bit." + }, + { OpName.RTI, + "Pulls the status register and return address from the stack, and jumps " + + "to the exact address pulled (note this is different from RTL/RTS)." + }, + { OpName.RTL, + "Pulls the 24-bit return address from the stack, increments it, and jumps to it." + }, + { OpName.RTS, + "Pulls the 16-bit return address from the stack, increments it, and jumps to it." + }, + { OpName.SBC, + "Subtracts the value specified by the operand from the contents of the " + + "accumulator, and leaves the result in the accumulator. Sets the carry, " + + "zero, and negative flags." + }, + { OpName.SEC, + "Sets the processor carry flag (C) to one." + }, + { OpName.SED, + "Sets the processor decimal flag (D) to one." + }, + { OpName.SEI, + "Sets the processor interrupt disable flag (I) to one." + }, + { OpName.SEP, + "Sets specific bits in the processor status register to one." + }, + { OpName.STA, + "Stores the value in the accumulator into memory." + }, + { OpName.STP, + "Stops the processor until a CPU reset occurs." + }, + { OpName.STX, + "Stores the value in the X register into memory." + }, + { OpName.STY, + "Stores the value in the Y register into memory." + }, + { OpName.STZ, + "Stores zero into memory." + }, + { OpName.TAX, + "Transfers the contents of the accumulator to the X register." + }, + { OpName.TAY, + "Transfers the contents of the accumulator to the Y register." + }, + { OpName.TCD, + "Transfers the 16-bit accumulator to the direct page register." + }, + { OpName.TCS, + "Transfers the 16-bit accumulator to the stack pointer register." + }, + { OpName.TDC, + "Transfers the direct page register to the 16-bit accumulator." + }, + { OpName.TRB, + "Logically ANDs the complement of the value in the accumulator with a value " + + "in memory, and stores it in memory. This can be used to clear specific bits " + + "in memory." + }, + { OpName.TSB, + "Logically ORs the value in the accumulator with a value in memory, and " + + "stores it in memory. This can be used to set specific bits in memory." + }, + { OpName.TSC, + "Transfers the stack pointer register to the 16-bit accumulator." + }, + { OpName.TSX, + "Transfers the stack pointer register to the X register." + }, + { OpName.TXA, + "Transfers the X register to the accumulator." + }, + { OpName.TXS, + "Transfers the X register to the stack pointer register." + }, + { OpName.TXY, + "Transfers the X register to the Y register." + }, + { OpName.TYA, + "Transfers the Y register to the accumulator." + }, + { OpName.TYX, + "Transfers the Y register to the X register." + }, + { OpName.WAI, + "Stalls the processor until an interrupt is received. If the interrupt " + + "disable flag (I) is set to one, execution will continue with the next " + + "instruction rather than calling through an interrupt vector." + }, + { OpName.WDM, + "Reserved for future expansion. (Behaves as a two-byte NOP.)" + }, + { OpName.XBA, + "Swaps the high and low bytes in the 16-bit accumulator. Sometimes referred " + + "to as SWA." + }, + { OpName.XCE, + "Exchanges carry and emulation bits." + }, + + // + // 6502 undocumented instructions. + // + // References: + // http://www.ffd2.com/fridge/docs/6502-NMOS.extra.opcodes + // http://nesdev.com/undocumented_opcodes.txt + // + { OpName.ANC, + "AND byte with accumulator. If result is negative then carry is set." + + "\r\n\r\nAlt mnemonic: AAC" + }, + { OpName.ANE, + "Transfer X register to accumulator, then AND accumulator with value." + + "\r\n\r\nAlt mnemonic: XAA" + }, + { OpName.ARR, + "AND byte with accumulator, then rotate one bit right. Equivalent to " + + "AND + ROR." + }, + { OpName.ASR, + "AND byte with accumulator, then shift right one bit. Equivalent to AND + LSR." + + "\r\n\r\nAlt mnemonic: ALR" + }, + { OpName.DCP, + "Decrement memory location, then compare result to accumulator. Equivalent " + + "to DEC + CMP." + + "\r\n\r\nAlt mnemonic: DCM" + }, + { OpName.DOP, + "Double-byte no-operation." + + "\r\n\r\nAlt mnemonic: NOP / SKB" + }, + { OpName.HLT, + "Crash the CPU, halting execution and ignoring interrupts." + + "\r\n\r\nAlt mnemonic: KIL / JAM" + }, + { OpName.ISB, + "Increment memory, then subtract memory from accumulator with borrow. " + + "Equivalent to INC + SBC." + + "\r\n\r\nAlt mnemonic: ISC / INS" + }, + { OpName.LAE, + "AND memory with stack pointer, then transfer result to accumulator, " + + "X register, and stack pointer. (Note: possibly unreliable.)" + + "\r\n\r\nAlt mnemonic: LAR / LAS" + }, + { OpName.LAX, + "Load accumulator and X register from memory. Equivalent to LDA + LDX." + }, + { OpName.LXA, + "ORs accumulator with a value, ANDs result with immediate value, then stores " + + "the result in accumulator and X register." + + "Equivalent to ORA + AND + TAX." + + "\r\n\r\nAlt mnemonic: ATX / OAL" + }, + { OpName.RLA, + "Rotate memory one bit left, then AND accumulator with memory. Equivalent " + + "to ROL + AND." + }, + { OpName.RRA, + "Rotate memory one bit right, then add accumulator to memory with carry. " + + "Equivalent to ROR + ADC." + }, + { OpName.SAX, + "AND X register with accumulator, without changing the contents of either " + + "register, subtract an immediate value, then store result in X register." + + "\r\n\r\nAlt mnemonic: AAX / AXS" + }, + { OpName.SBX, + "AND X register with accumulator and transfer to X register, then " + + "subtract byte from X register without borrow." + + "\r\n\r\nAlt mnemonic: AXS / SAX" + }, + { OpName.SHA, + "AND X register with accumulator, then AND result with 7 and store." + + "\r\n\r\nAlt mnemonic: AXA" + }, + { OpName.SHS, + "AND X register with accumulator, without changing the contents of either" + + "register, and transfer to stack pointer. Then " + + "AND stack pointer with high byte of operand + 1." + + "\r\n\r\nAlt mnemonic: XAS / TAS" + }, + { OpName.SHX, + "AND X register with the high byte of the argument + 1, and store the result." + + "\r\n\r\nAlt mnemonic: SXA / XAS" + }, + { OpName.SHY, + "AND Y register with the high byte of the argument + 1, and store the result." + + "\r\n\r\nAlt mnemonic: SYA / SAY" + }, + { OpName.SLO, + "Shift memory left one bit, then OR accumulator with memory. Equivalent to " + + "ASL + ORA." + + "\r\n\r\nAlt mnemonic: ASO" + }, + { OpName.SRE, + "Shift memory right one bit, then EOR accumulator with memory. Equivalent to " + + "LSR + EOR." + + "\r\n\r\nAlt mnemonic: LSE" + }, + { OpName.TOP, + "Triple-byte no-operation. This actually performs a load." + + "\r\n\r\nAlt mnemonic: NOP / SKW" + }, + + // + // 65C02 undocumented instructions. + // + { OpName.LDD, + "Load and Discard. Usually a no-op, but the activity on the address bus " + + "can affect memory-mapped I/O." + }, + }; + + /// + /// Address mode short descriptions, USA English. + /// + private static Dictionary sAddrMode_enUS = + new Dictionary() { + { OpDef.AddressMode.Abs, "Absolute" }, + { OpDef.AddressMode.AbsInd, "Absolute Indirect" }, + { OpDef.AddressMode.AbsIndLong, "Absolute Indirect Long" }, + { OpDef.AddressMode.AbsIndexX, "Absolute Indexed X" }, + { OpDef.AddressMode.AbsIndexXInd, "Absolute Indexed X Indirect" }, + { OpDef.AddressMode.AbsIndexXLong, "Absolute Indexed X Long" }, + { OpDef.AddressMode.AbsIndexY, "Absolute Indexed Y" }, + { OpDef.AddressMode.AbsLong, "Absolute Long" }, + { OpDef.AddressMode.Acc, "Accumulator" }, + { OpDef.AddressMode.BlockMove, "Block Move" }, + { OpDef.AddressMode.DP, "Direct Page" }, + { OpDef.AddressMode.DPInd, "Direct Page Indirect" }, + { OpDef.AddressMode.DPIndIndexY, "Direct Page Indirect Indexed Y" }, + { OpDef.AddressMode.DPIndIndexYLong, "Direct Page Indirect Indexed Y Long" }, + { OpDef.AddressMode.DPIndLong, "Direct Page Indirect Long" }, + { OpDef.AddressMode.DPIndexX, "Direct Page Indexed X" }, + { OpDef.AddressMode.DPIndexXInd, "Direct Page Indexed X Indirect" }, + { OpDef.AddressMode.DPIndexY, "Direct Page Indexed Y" }, + { OpDef.AddressMode.Imm, "Immediate" }, + { OpDef.AddressMode.ImmLongA, "Immediate" }, + { OpDef.AddressMode.ImmLongXY, "Immediate" }, + { OpDef.AddressMode.Implied, "" }, + { OpDef.AddressMode.PCRel, "PC Relative" }, + { OpDef.AddressMode.PCRelLong, "PC Relative Long" }, + { OpDef.AddressMode.StackAbs, "Stack Absolute" }, + { OpDef.AddressMode.StackDPInd, "Stack Direct Page Indirect" }, + { OpDef.AddressMode.StackInt, "" }, + { OpDef.AddressMode.StackPCRelLong, "Stack PC Relative Long" }, + { OpDef.AddressMode.StackPull, "" }, + { OpDef.AddressMode.StackPush, "" }, + { OpDef.AddressMode.StackRTI, "" }, + { OpDef.AddressMode.StackRTL, "" }, + { OpDef.AddressMode.StackRTS, "" }, + { OpDef.AddressMode.StackRel, "" }, + { OpDef.AddressMode.StackRelIndIndexY, "Stack Relative Indirect Index Y" }, + { OpDef.AddressMode.WDM, "" } + }; + + /// + /// Cycle modifier descriptions. These are intended to be very terse. + /// + private static Dictionary sCycleMod_enUS = + new Dictionary() { + { OpDef.CycleMod.OneIfM0, "+1 if M=0" }, + { OpDef.CycleMod.TwoIfM0, "+2 if M=0" }, + { OpDef.CycleMod.OneIfX0, "+1 if X=0" }, + { OpDef.CycleMod.OneIfDpNonzero, "+1 if DL != 0" }, + { OpDef.CycleMod.OneIfIndexPage, "+1 if index across page" }, + { OpDef.CycleMod.OneIfD1, "+1 if D=1 on 65C02" }, + { OpDef.CycleMod.OneIfBranchTaken, "+1 if branch taken" }, + { OpDef.CycleMod.OneIfBranchPage, "+1 if branch across page unless E=0" }, + { OpDef.CycleMod.OneIfE0, "+1 if E=0" }, + { OpDef.CycleMod.OneIf65C02, "+1 if 65C02" }, + { OpDef.CycleMod.MinusOneIfNoPage, "-1 if 65C02 and not across page" }, + { OpDef.CycleMod.BlockMove, "+7 per byte" }, + }; + } +} diff --git a/Asm65/OpName.cs b/Asm65/OpName.cs new file mode 100644 index 0000000..73de99e --- /dev/null +++ b/Asm65/OpName.cs @@ -0,0 +1,148 @@ +/* + * Copyright 2018 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; + +namespace Asm65 { + /// + /// String constants for opcodes. These are not (and should not be) localized. They + /// must be lower-case. + /// + public static class OpName { + // NOTE: these all happen to be three characters, but I don't think we want to + // guarantee that. On the 65816 some mnemonics are extended (e.g. LDAL for LDA with + // a 24-bit operand), but that's assembler-specific and handled elsewhere. + public const string Unknown = "???"; + public const string ADC = "adc"; + public const string AND = "and"; + public const string ASL = "asl"; + public const string BCC = "bcc"; + public const string BCS = "bcs"; + public const string BEQ = "beq"; + public const string BIT = "bit"; + public const string BMI = "bmi"; + public const string BNE = "bne"; + public const string BPL = "bpl"; + public const string BRA = "bra"; + public const string BRK = "brk"; + public const string BRL = "brl"; + public const string BVC = "bvc"; + public const string BVS = "bvs"; + public const string CLC = "clc"; + public const string CLD = "cld"; + public const string CLI = "cli"; + public const string CLV = "clv"; + public const string CMP = "cmp"; + public const string COP = "cop"; + public const string CPX = "cpx"; + public const string CPY = "cpy"; + public const string DEC = "dec"; + public const string DEX = "dex"; + public const string DEY = "dey"; + public const string EOR = "eor"; + public const string INC = "inc"; + public const string INX = "inx"; + public const string INY = "iny"; + public const string JML = "jml"; + public const string JMP = "jmp"; + public const string JSL = "jsl"; + public const string JSR = "jsr"; + public const string LDA = "lda"; + public const string LDX = "ldx"; + public const string LDY = "ldy"; + public const string LSR = "lsr"; + public const string MVN = "mvn"; + public const string MVP = "mvp"; + public const string NOP = "nop"; + public const string ORA = "ora"; + public const string PEA = "pea"; + public const string PEI = "pei"; + public const string PER = "per"; + public const string PHA = "pha"; + public const string PHB = "phb"; + public const string PHD = "phd"; + public const string PHK = "phk"; + public const string PHP = "php"; + public const string PHX = "phx"; + public const string PHY = "phy"; + public const string PLA = "pla"; + public const string PLB = "plb"; + public const string PLD = "pld"; + public const string PLP = "plp"; + public const string PLX = "plx"; + public const string PLY = "ply"; + public const string REP = "rep"; + public const string ROL = "rol"; + public const string ROR = "ror"; + public const string RTI = "rti"; + public const string RTL = "rtl"; + public const string RTS = "rts"; + public const string SBC = "sbc"; + public const string SEC = "sec"; + public const string SED = "sed"; + public const string SEI = "sei"; + public const string SEP = "sep"; + public const string STA = "sta"; + public const string STP = "stp"; + public const string STX = "stx"; + public const string STY = "sty"; + public const string STZ = "stz"; + public const string TAX = "tax"; + public const string TAY = "tay"; + public const string TCD = "tcd"; + public const string TCS = "tcs"; + public const string TDC = "tdc"; + public const string TRB = "trb"; + public const string TSB = "tsb"; + public const string TSC = "tsc"; + public const string TSX = "tsx"; + public const string TXA = "txa"; + public const string TXS = "txs"; + public const string TXY = "txy"; + public const string TYA = "tya"; + public const string TYX = "tyx"; + public const string WAI = "wai"; + public const string WDM = "wdm"; + public const string XBA = "xba"; + public const string XCE = "xce"; + + // Undocumented 6502 instructions. + public const string ANC = "anc"; + public const string ANE = "ane"; + public const string ARR = "arr"; + public const string ASR = "asr"; + public const string DCP = "dcp"; + public const string DOP = "dop"; + public const string HLT = "hlt"; + public const string ISB = "isb"; + public const string LAE = "lae"; + public const string LAX = "lax"; + public const string LXA = "lxa"; + public const string RLA = "rla"; + public const string RRA = "rra"; + public const string SAX = "sax"; + public const string SBX = "sbx"; + public const string SHA = "sha"; + public const string SHS = "shs"; + public const string SHX = "shx"; + public const string SHY = "shy"; + public const string SLO = "slo"; + public const string SRE = "sre"; + public const string TOP = "top"; + + // Undocumented 65C02 instructions. + public const string LDD = "ldd"; + } +} diff --git a/Asm65/Properties/Resources.Designer.cs b/Asm65/Properties/Resources.Designer.cs new file mode 100644 index 0000000..3f729e3 --- /dev/null +++ b/Asm65/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Asm65.Properties { + using System; + using System.Reflection; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Asm65.Properties.Resources", typeof(Resources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Fourth time. + /// + public static string TEST_STRING { + get { + return ResourceManager.GetString("TEST_STRING", resourceCulture); + } + } + } +} diff --git a/Asm65/Properties/Resources.resx b/Asm65/Properties/Resources.resx new file mode 100644 index 0000000..bd42ef5 --- /dev/null +++ b/Asm65/Properties/Resources.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Fourth time + + \ No newline at end of file diff --git a/Asm65/StatusFlags.cs b/Asm65/StatusFlags.cs new file mode 100644 index 0000000..77417f3 --- /dev/null +++ b/Asm65/StatusFlags.cs @@ -0,0 +1,328 @@ +/* + * Copyright 2018 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.Text; + +namespace Asm65 { + /// + /// Status flag holder. Each flag may be known to be zero, known to be one, or + /// hold an indeterminate value (represented as a negative number). + /// + /// For the 65802/65816, we also keep track of the E flag (emulation bit), even though + /// that's not actually held in the P register. + /// + /// Note this is a value type, not a reference type. + /// + /// The default value is UNSPECIFIED for all bits. + /// + public struct StatusFlags { + private TriState16 mState; + + + /// + /// Flag bits, from processor status register definition. The 'e' (emulation) + /// flag from the 65816 is tacked onto the end. + /// + /// The enumerated value matches the bit number in the P register. + /// + public enum FlagBits { + C = 0, + Z = 1, + I = 2, + D = 3, + B = 4, // all CPUs except 65802/65816 in native mode + X = 4, // 65802/65816 in native mode + M = 5, // 65802/65816 in native mode (always 1 on other CPUs) + V = 6, + N = 7, + E = 8 // not actually part of P-reg; accessible only through XCE + } + + /// + /// Default value (all flags UNSPECIFIED). A newly-created array of StatusFlags will + /// all have this value. + /// + public static readonly StatusFlags DefaultValue = + new StatusFlags { mState = new TriState16(0, 0) }; + + /// + /// All flags are INDETERMINATE. + /// + public static readonly StatusFlags AllIndeterminate = + new StatusFlags() { mState = new TriState16(0x01ff, 0x01ff) }; + + public int C { + get { + return mState.GetBit((int)FlagBits.C); + } + set { + if (value == 0) { + mState.SetZero((int)FlagBits.C); + } else if (value == 1) { + mState.SetOne((int)FlagBits.C); + } else if (value == TriState16.UNSPECIFIED) { + mState.SetUnspecified((int)FlagBits.C); + } else { + mState.SetIndeterminate((int)FlagBits.C); + } + } + } + + public int Z { + get { + return mState.GetBit((int)FlagBits.Z); + } + set { + if (value == 0) { + mState.SetZero((int)FlagBits.Z); + } else if (value == 1) { + mState.SetOne((int)FlagBits.Z); + } else if (value == TriState16.UNSPECIFIED) { + mState.SetUnspecified((int)FlagBits.Z); + } else { + mState.SetIndeterminate((int)FlagBits.Z); + } + } + } + + public int I { + get { + return mState.GetBit((int)FlagBits.I); + } + set { + if (value == 0) { + mState.SetZero((int)FlagBits.I); + } else if (value == 1) { + mState.SetOne((int)FlagBits.I); + } else if (value == TriState16.UNSPECIFIED) { + mState.SetUnspecified((int)FlagBits.I); + } else { + mState.SetIndeterminate((int)FlagBits.I); + } + } + } + + public int D { + get { + return mState.GetBit((int)FlagBits.D); + } + set { + if (value == 0) { + mState.SetZero((int)FlagBits.D); + } else if (value == 1) { + mState.SetOne((int)FlagBits.D); + } else if (value == TriState16.UNSPECIFIED) { + mState.SetUnspecified((int)FlagBits.D); + } else { + mState.SetIndeterminate((int)FlagBits.D); + } + } + } + + public int X { + get { + return mState.GetBit((int)FlagBits.X); + } + set { + if (value == 0) { + mState.SetZero((int)FlagBits.X); + } else if (value == 1) { + mState.SetOne((int)FlagBits.X); + } else if (value == TriState16.UNSPECIFIED) { + mState.SetUnspecified((int)FlagBits.X); + } else { + mState.SetIndeterminate((int)FlagBits.X); + } + } + } + + public int M { + get { + return mState.GetBit((int)FlagBits.M); + } + set { + if (value == 0) { + mState.SetZero((int)FlagBits.M); + } else if (value == 1) { + mState.SetOne((int)FlagBits.M); + } else if (value == TriState16.UNSPECIFIED) { + mState.SetUnspecified((int)FlagBits.M); + } else { + mState.SetIndeterminate((int)FlagBits.M); + } + } + } + + public int V { + get { + return mState.GetBit((int)FlagBits.V); + } + set { + if (value == 0) { + mState.SetZero((int)FlagBits.V); + } else if (value == 1) { + mState.SetOne((int)FlagBits.V); + } else if (value == TriState16.UNSPECIFIED) { + mState.SetUnspecified((int)FlagBits.V); + } else { + mState.SetIndeterminate((int)FlagBits.V); + } + } + } + + public int N { + get { + return mState.GetBit((int)FlagBits.N); + } + set { + if (value == 0) { + mState.SetZero((int)FlagBits.N); + } else if (value == 1) { + mState.SetOne((int)FlagBits.N); + } else if (value == TriState16.UNSPECIFIED) { + mState.SetUnspecified((int)FlagBits.N); + } else { + mState.SetIndeterminate((int)FlagBits.N); + } + } + } + + public int E { + get { + return mState.GetBit((int)FlagBits.E); + } + set { + if (value == 0) { + mState.SetZero((int)FlagBits.E); + } else if (value == 1) { + mState.SetOne((int)FlagBits.E); + } else if (value == TriState16.UNSPECIFIED) { + mState.SetUnspecified((int)FlagBits.E); + } else { + mState.SetIndeterminate((int)FlagBits.E); + } + } + } + + public int GetBit(FlagBits index) { + return mState.GetBit((int) index); + } + + /// + /// Returns true if the current processor status flags are configured for a short + /// (8-bit) accumulator. + /// + public bool ShortM { + get { + // E==1 --> true (we're in emulation mode) + // E==0 || E==? : native / assumed native + // M==1 || M==? --> true (native mode, configured short or assumed short) + // M==0 --> false (native mode, configured long) + return (E == 1) || (M != 0); + } + } + + /// + /// Returns true if the current processor status flags are configured for short + /// (8-bit) X/Y registers. + /// + public bool ShortX { + get { + // (same logic as ShortM) + return (E == 1) || (X != 0); + } + } + + /// + /// Access the value as a single integer. Used for serialization. + /// + public int AsInt { + get { + return mState.AsInt; + } + } + + /// + /// Set the value from an integer. Used for serialization. + /// + public static StatusFlags FromInt(int value) { + if ((value & ~0x01ff01ff) != 0) { + throw new InvalidOperationException("Bad StatusFlags value " + + value.ToString("x8")); + } + StatusFlags newFlags = new StatusFlags(); + newFlags.mState.AsInt = value; + return newFlags; + } + + /// + /// Merge a set of status flags into this one. + /// + public void Merge(StatusFlags other) { + mState.Merge(other.mState); + } + + /// + /// Applies flags, overwriting existing values. This will set one or more flags + /// to 0, 1, or indeterminate. Unspecified (0/0) values have no effect. + /// + /// This is useful when merging "overrides" in. + /// + public void Apply(StatusFlags overrides) { + mState.Apply(overrides.mState); + } + + /// + /// Returns a string representation of the flags. + /// + /// If set, include the 'E' flag, and show M/X. + public string ToString(bool showMXE) { + StringBuilder sb = new StringBuilder(showMXE ? 10 : 8); + sb.Append("-?nN"[N + 2]); + sb.Append("-?vV"[V + 2]); + sb.Append(showMXE ? "-?mM"[M + 2] : '-'); + sb.Append(showMXE ? "-?xX"[X + 2] : '-'); + sb.Append("-?dD"[D + 2]); + sb.Append("-?iI"[I + 2]); + sb.Append("-?zZ"[Z + 2]); + sb.Append("-?cC"[C + 2]); + if (showMXE) { + sb.Append(' '); + sb.Append("-?eE"[E + 2]); + } + return sb.ToString(); + } + + + public static bool operator ==(StatusFlags a, StatusFlags b) { + return a.mState == b.mState; + } + public static bool operator !=(StatusFlags a, StatusFlags b) { + return !(a == b); + } + public override bool Equals(object obj) { + return obj is StatusFlags && this == (StatusFlags)obj; + } + public override int GetHashCode() { + return mState.GetHashCode(); + } + + public override string ToString() { + return ToString(true); + // + " [" + mState.ToString() + "]" + } + } +} diff --git a/Asm65/TriState16.cs b/Asm65/TriState16.cs new file mode 100644 index 0000000..571d6d4 --- /dev/null +++ b/Asm65/TriState16.cs @@ -0,0 +1,172 @@ +/* + * Copyright 2018 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.Diagnostics; + +namespace Asm65 { + /// + /// A value with 16 tri-state bits. + /// + public struct TriState16 { + /// + /// Two 16-bit values. The low 16 bits indicate that bit N is zero, the high + /// 16 bits indicate that bit N is one. If neither or both are set, the value + /// is undetermined and could be either. + /// + /// While 0/0 and 1/1 both represent an indeterminate value, they're not equivalent. + /// When two values are merged, a 0/0 bit has no effect on the result, while + /// a 1/1 bit will force the merged bit to be indeterminate. We use UNSPECIFIED for + /// 0/0 and INDETERMINATE for 1/1. + /// + /// The default value for new instances is UNSPECIFIED for all bits. + /// + private ushort mZero, mOne; + + public const int INDETERMINATE = -1; + public const int UNSPECIFIED = -2; + + /// + /// Constructor; sets initial zero/one values. + /// + /// 16-bit value, with bits set for each known-zero. + /// 16-bit value, with bits set for each known-one. + public TriState16(ushort zeroes, ushort ones) { + mZero = zeroes; + mOne = ones; + } + + /// + /// Access the value as a single integer. Used for serialization. + /// + public int AsInt { + get { + return (int)mZero | ((int)mOne << 16); + } + set { + mZero = (ushort) value; + mOne = (ushort) (value >> 16); + } + } + + /// + /// Sets bit N to zero. + /// + public void SetZero(int bit) { + Debug.Assert(bit >= 0 && bit < 16); + + // clear 1-flag, set 0-flag + //mValue = (mValue & ~(1U << (bit + 16))) | (1U << bit); + mZero |= (ushort) (1U << bit); + mOne &= (ushort) ~(1U << bit); + } + + /// + /// Sets bit N to one. + /// + public void SetOne(int bit) { + Debug.Assert(bit >= 0 && bit < 16); + + // clear 0-flag, set 1-flag + //mValue = (mValue & ~(1U << bit)) | (1U << (bit + 16)); + mZero &= (ushort)~(1U << bit); + mOne |= (ushort)(1U << bit); + } + + /// + /// Sets bit N to indeterminate. + /// + public void SetIndeterminate(int bit) { + Debug.Assert(bit >= 0 && bit < 16); + + // set both flags + mZero |= (ushort)(1U << bit); + mOne |= (ushort)(1U << bit); + } + + /// + /// Sets bit N to unspecified. + /// + public void SetUnspecified(int bit) { + Debug.Assert(bit >= 0 && bit < 16); + + // clear both flags + mZero &= (ushort)~(1U << bit); + mOne &= (ushort)~(1U << bit); + } + + /// + /// Merges bit states. + /// + /// Value to merge in. + public void Merge(TriState16 other) { + //mValue |= other.mValue; + mZero |= other.mZero; + mOne |= other.mOne; + } + + /// + /// Applies a set of bits to an existing set, overriding any bits that aren't set + /// to "unspecified" in the input. + /// + public void Apply(TriState16 overrides) { + ushort mask = (ushort) ~(overrides.mZero | overrides.mOne); + mZero = (ushort)((mZero & mask) | overrides.mZero); + mOne = (ushort)((mOne & mask) | overrides.mOne); + } + + /// + /// Returns 0, 1, -1, or -2 depending on whether the specified bit is 0, 1, + /// indeterminate, or unspecified. + /// + public int GetBit(int bit) { + bool zero = ((mZero >> bit) & 0x01) != 0; + bool one = ((mOne >> bit) & 0x01) != 0; + if (zero ^ one) { + // Only one of the bits is set. + if (one) { + return 1; + } else { + return 0; + } + } else { + // Both or neither are set. + if (zero) { + return INDETERMINATE; + } else { + return UNSPECIFIED; + } + } + } + + + public static bool operator ==(TriState16 a, TriState16 b) { + return a.mZero == b.mZero && a.mOne == b.mOne; + } + public static bool operator !=(TriState16 a, TriState16 b) { + return !(a == b); + } + public override bool Equals(object obj) { + return obj is TriState16 && this == (TriState16)obj; + } + public override int GetHashCode() { + return (mOne << 16) | mZero; + } + + public override string ToString() { + return mZero.ToString("x4") + "-" + mOne.ToString("x4"); + } + } +} diff --git a/CodeLab/App.config b/CodeLab/App.config new file mode 100644 index 0000000..731f6de --- /dev/null +++ b/CodeLab/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CodeLab/CodeLab.csproj b/CodeLab/CodeLab.csproj new file mode 100644 index 0000000..0bba042 --- /dev/null +++ b/CodeLab/CodeLab.csproj @@ -0,0 +1,84 @@ + + + + + Debug + AnyCPU + {3B66ABD8-7129-4D2B-B48E-B03FEC835CE1} + WinExe + CodeLab + CodeLab + v4.6.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + Form + + + MainWindow.cs + + + + + MainWindow.cs + Designer + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/CodeLab/Form1.resx b/CodeLab/Form1.resx new file mode 100644 index 0000000..75db02c --- /dev/null +++ b/CodeLab/Form1.resx @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 132, 17 + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG + YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9 + 0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw + bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc + VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9 + c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32 + Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo + mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+ + kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D + TgDQASA1MVpwzwAAAABJRU5ErkJggg== + + + \ No newline at end of file diff --git a/CodeLab/MainWindow.Designer.cs b/CodeLab/MainWindow.Designer.cs new file mode 100644 index 0000000..a5641bb --- /dev/null +++ b/CodeLab/MainWindow.Designer.cs @@ -0,0 +1,46 @@ +namespace WorkBench +{ + partial class MainWindow + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // MainWindow + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(284, 261); + this.Name = "MainWindow"; + this.Text = "6502bench"; + this.ResumeLayout(false); + + } + + #endregion + } +} \ No newline at end of file diff --git a/CodeLab/MainWindow.cs b/CodeLab/MainWindow.cs new file mode 100644 index 0000000..359f39e --- /dev/null +++ b/CodeLab/MainWindow.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace WorkBench +{ + public partial class MainWindow : Form + { + public MainWindow() + { + InitializeComponent(); + } + } +} diff --git a/CodeLab/MainWindow.resx b/CodeLab/MainWindow.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/CodeLab/MainWindow.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/CodeLab/Program.cs b/CodeLab/Program.cs new file mode 100644 index 0000000..8bfd5f0 --- /dev/null +++ b/CodeLab/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace WorkBench +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainWindow()); + } + } +} diff --git a/CodeLab/Properties/AssemblyInfo.cs b/CodeLab/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..fbf61d5 --- /dev/null +++ b/CodeLab/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WorkBench")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("WorkBench")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3b66abd8-7129-4d2b-b48e-b03fec835ce1")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CodeLab/Properties/Resources.Designer.cs b/CodeLab/Properties/Resources.Designer.cs new file mode 100644 index 0000000..4b8ae6a --- /dev/null +++ b/CodeLab/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CodeLab.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CodeLab.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/CodeLab/Properties/Resources.resx b/CodeLab/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/CodeLab/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/CodeLab/Properties/Settings.Designer.cs b/CodeLab/Properties/Settings.Designer.cs new file mode 100644 index 0000000..99d3a5e --- /dev/null +++ b/CodeLab/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CodeLab.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.6.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/CodeLab/Properties/Settings.settings b/CodeLab/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/CodeLab/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/CodeLab/README.md b/CodeLab/README.md new file mode 100644 index 0000000..b8ff772 --- /dev/null +++ b/CodeLab/README.md @@ -0,0 +1 @@ +This is just a place-holder. Nothing to see here. diff --git a/CommonUtil/CRC32.cs b/CommonUtil/CRC32.cs new file mode 100644 index 0000000..c492bb7 --- /dev/null +++ b/CommonUtil/CRC32.cs @@ -0,0 +1,109 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.IO; + +namespace CommonUtil { + /// + /// Compute a standard CRC-32 (polynomial 0xedb88320, or + /// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1). + /// + public static class CRC32 { + private static readonly uint[] sTable = ComputeTable(); + + private const uint INVERT = 0xffffffff; + + /// + /// Generates 256-entry CRC table. + /// + /// Table. + private static uint[] ComputeTable() { + uint[] table = new uint[256]; + + uint poly = 0xedb88320; + for (int i = 0; i < 256; i++) { + uint val = (uint) i; + for (int j = 0; j < 8; j++) { + val = (val & 1) != 0 ? poly ^ (val >> 1) : val >> 1; + } + table[i] = val; + } + + return table; + } + + /// + /// Computes a CRC on part of a buffer of data. + /// + /// Previously computed CRC value. Initially zero. + /// Data to compute CRC on. + /// Start offset within buffer. + /// Number of bytes to process. + /// New CRC value. + public static uint OnBuffer(uint crc, byte[] buffer, int offset, int count) { + crc = crc ^ INVERT; + while (count-- != 0) { + crc = sTable[(crc ^ buffer[offset]) & 0xff] ^ (crc >> 8); + offset++; + } + return crc ^ INVERT; + } + + /// + /// Computes a CRC on a buffer of data. + /// + /// Previously computed CRC value. Initially zero. + /// Data to compute CRC on. + /// New CRC value. + public static uint OnWholeBuffer(uint crc, byte[] buffer) { + return OnBuffer(crc, buffer, 0, buffer.Length); + } + + /// + /// Computes a CRC on an entire file. + /// + /// Full path to file to open. + /// Receives the CRC. + /// True on success, false on file error. + public static bool OnWholeFile(string pathName, out uint ocrc) { + try { + using (FileStream fs = File.Open(pathName, FileMode.Open, FileAccess.Read)) { + byte[] buffer = new byte[8192]; + uint crc = 0; + long remain = fs.Length; + while (remain != 0) { + int toRead = (remain < buffer.Length) ? (int)remain : buffer.Length; + int actual = fs.Read(buffer, 0, toRead); + if (toRead != actual) { + throw new IOException("Expected " + toRead + ", got " + actual); + } + + crc = OnBuffer(crc, buffer, 0, toRead); + remain -= toRead; + } + + ocrc = crc; + return true; + } + } catch (IOException ioe) { + Debug.WriteLine("Fail: " + ioe); + ocrc = 0; + return false; + } + } + } +} diff --git a/CommonUtil/CommonUtil.csproj b/CommonUtil/CommonUtil.csproj new file mode 100644 index 0000000..93850c8 --- /dev/null +++ b/CommonUtil/CommonUtil.csproj @@ -0,0 +1,22 @@ + + + + netstandard2.0 + + + + + True + True + Resources.resx + + + + + + PublicResXFileCodeGenerator + Resources.Designer.cs + + + + diff --git a/CommonUtil/Container.cs b/CommonUtil/Container.cs new file mode 100644 index 0000000..9974fa5 --- /dev/null +++ b/CommonUtil/Container.cs @@ -0,0 +1,40 @@ +/* + * Copyright 2018 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.Linq; + +namespace CommonUtil { + public class Container { + /// + /// Compares two lists of strings to see if their contents are equal. The lists + /// must contain the same strings, in the same order. + /// + /// List #1. + /// List #2. + /// String comparer (e.g. StringComparer.InvariantCulture). If + /// null, the default string comparer is used. + /// True if the lists are equal. + public static bool StringListEquals(IList l1, IListl2, + StringComparer comparer) { + // Quick check for reference equality. + if (l1 == l2) { + return true; + } + return Enumerable.SequenceEqual(l1, l2, comparer); + } + } +} diff --git a/CommonUtil/DebugLog.cs b/CommonUtil/DebugLog.cs new file mode 100644 index 0000000..c74096a --- /dev/null +++ b/CommonUtil/DebugLog.cs @@ -0,0 +1,233 @@ +/* + * Copyright 2018 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.IO; +using System.Text; + +namespace CommonUtil { + /// + /// Debug log facility, with priority levels and time stamps. + /// + /// The logs are held in memory. The internal storage expands as logs are added + /// until the maximum size is reached, then switches to circular buffering. This + /// minimizes overhead for small logs while avoiding infinite expansion. + /// + public class DebugLog { + /// + /// Log priority levels, in ascending order. "Silent" is only used as an argument + /// when setting the minimum priority level. + /// + public enum Priority { + Verbose = 0, Debug, Info, Warning, Error, Silent + } + + private static char[] sSingleLetter = { 'V', 'D', 'I', 'W', 'E', 'S' }; + + /// + /// Holds a single log entry. + /// + private struct LogEntry { + public DateTime mWhen; + public Priority mPriority; + public string mText; + + public LogEntry(Priority prio, string msg) { + mWhen = DateTime.Now; + mPriority = prio; + mText = msg; + } + } + + /// + /// Log collection. + /// + private List mEntries = new List(); + private int mTopEntry = 0; + + /// + /// Date/time when the log object was created. Used for relative time display mode. + /// + private DateTime mStartWhen; + + /// + /// If set, display time stamps as relative time rather than absolute. + /// + private bool mShowRelTime = false; + + /// + /// Minimum priority level. Anything below this is ignored. + /// + private Priority mMinPriority = Priority.Debug; + + /// + /// Maximum number of lines we'll hold in memory. This is a simple measure + /// to keep the process from expanding without bound. + /// + private int mMaxLines = 100000; + + /// + /// Constructor. Configures min priority to Info. + /// + public DebugLog() : this(Priority.Info) { } + + /// + /// Constructor. + /// + /// Minimum log priority level. + public DebugLog(Priority prio) { + mMinPriority = prio; + mStartWhen = DateTime.Now; + LogI("Log started at " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss zzz")); + } + + /// + /// Sets the message priority threshold. Messages below the specified priority + /// will be ignored. + /// + /// Minimum priority value. + public void SetMinPriority(Priority prio) { + mMinPriority = prio; + } + + /// + /// Sets the "show relative time" flag. If set, the timestamp in the log is + /// relative to when the log object was created, instead of wall-clock time. + /// + /// + public void SetShowRelTime(bool showRelTime) { + mShowRelTime = showRelTime; + } + + /// + /// Returns true if a message logged at the specified priority would be accepted. + /// + /// + /// + public bool IsLoggable(Priority prio) { + return prio >= mMinPriority; + } + + /// + /// Clears all entries. + /// + public void Clear() { + mEntries.Clear(); + } + + /// + /// Adds a message to the log buffer. + /// + /// Log priority. + /// Message to log. + public void Log(Priority prio, string message) { + if (prio < mMinPriority) { + return; + } + LogEntry ent = new LogEntry(prio, message); + if (mEntries.Count < mMaxLines) { + // Still growing. + mEntries.Add(ent); + } else { + // Circular replacement. Adding to the end then removing [0] has + // significant performance issues. + mEntries[mTopEntry++] = ent; + if (mTopEntry == mMaxLines) { + mTopEntry = 0; + } + } + } + + public void LogV(string message) { + Log(Priority.Verbose, message); + } + public void LogD(string message) { + Log(Priority.Debug, message); + } + public void LogI(string message) { + Log(Priority.Info, message); + } + public void LogW(string message) { + Log(Priority.Warning, message); + } + public void LogE(string message) { + Log(Priority.Error, message); + } + + /// + /// Dumps the contents of the log to a file. + /// + /// Full or partial pathname. + public void WriteToFile(string pathName) { + StringBuilder sb = new StringBuilder(); + using (StreamWriter sw = new StreamWriter(pathName, false, Encoding.UTF8)) { + for (int i = mTopEntry; i < mEntries.Count; i++) { + WriteEntry(sw, mEntries[i], sb); + } + for (int i = 0; i < mTopEntry; i++) { + WriteEntry(sw, mEntries[i], sb); + } + } + } + + /// + /// Writes a single entry to a file. Pass in a StringBuilder so we don't have + /// to create a new one every time. + /// + private void WriteEntry(StreamWriter sw, LogEntry ent, StringBuilder sb) { + sb.Clear(); + FormatEntry(ent, sb); + sw.WriteLine(sb.ToString()); + } + + /// + /// Formats an entry, appending the text to the provided StringBuilder. + /// + private void FormatEntry(LogEntry ent, StringBuilder sb) { + if (mShowRelTime) { + sb.Append((ent.mWhen - mStartWhen).ToString(@"mm\:ss\.fff")); + } else { + sb.Append(ent.mWhen.ToString(@"hh\:mm\:ss\.fff")); + } + sb.Append(' '); + sb.Append(sSingleLetter[(int)ent.mPriority]); + sb.Append(' '); + sb.Append(ent.mText); + } + + /// + /// Dumps the contents of the log to a string. This is intended for display in a + /// text box, so lines are separated with CRLF. + /// + /// + public string WriteToString() { + StringBuilder sb = new StringBuilder(); + for (int i = mTopEntry; i < mEntries.Count; i++) { + FormatEntry(mEntries[i], sb); + sb.Append("\r\n"); + } + for (int i = 0; i < mTopEntry; i++) { + FormatEntry(mEntries[i], sb); + sb.Append("\r\n"); + } + return sb.ToString(); + } + + public override string ToString() { + return "DebugLog has " + mEntries.Count + " entries"; + } + } +} diff --git a/CommonUtil/FileLoadReport.cs b/CommonUtil/FileLoadReport.cs new file mode 100644 index 0000000..1efc3b2 --- /dev/null +++ b/CommonUtil/FileLoadReport.cs @@ -0,0 +1,136 @@ +/* + * Copyright 2018 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; +using System.Collections.Generic; +using System.Text; + +namespace CommonUtil { + /// + /// File load item, identifying the location, severity, and details of the issue. + /// + public class FileLoadItem { + public const int NO_LINE = -1; + public const int NO_COLUMN = -1; + + public enum Type { + Unknown = 0, + Notice, + Warning, + Error + } + + public int Line { get; private set; } + public int Column { get; private set; } + public Type MsgType { get; private set; } + public string Message { get; private set; } + + public FileLoadItem(int line, int col, Type msgType, string msg) { + Line = line; + Column = col; + MsgType = msgType; ; + Message = msg; + } + } + + /// + /// A structured collection of errors and warnings generated when reading data from a file. + /// + public class FileLoadReport : IEnumerable { + // List of items. Currently unsorted; items will appear in the order they were added. + private List mItems = new List(); + + public string FileName { get; private set; } + public bool HasWarnings { get; private set; } + public bool HasErrors { get; private set; } + + /// + /// Constructor. + /// + /// Name of file being loaded. This is just for output; this + /// class doesn't actually try to access the file. + public FileLoadReport(string fileName) { + FileName = fileName; + } + + public IEnumerator GetEnumerator() { + return mItems.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() { + return mItems.GetEnumerator(); + } + + public int Count { get { return mItems.Count; } } + + /// + /// Adds a new message to the report. + /// + /// Is this a warning or an error? + /// Human-readable message. + public void Add(FileLoadItem.Type msgType, string msg) { + Add(FileLoadItem.NO_LINE, FileLoadItem.NO_COLUMN, msgType, msg); + } + + /// + /// Adds a new message to the report. + /// + /// Line where the issue was seen. + /// Column where the problem starts, or NO_COLUMN. + /// Is this a warning or an error? + /// Human-readable message. + public void Add(int line, int col, FileLoadItem.Type msgType, string msg) { + mItems.Add(new FileLoadItem(line, col, msgType, msg)); + switch (msgType) { + case FileLoadItem.Type.Warning: + HasWarnings = true; + break; + case FileLoadItem.Type.Error: + HasErrors = true; + break; + } + } + + /// + /// Formats the entire collection into a single multi-line string. + /// + /// Formatted string. + public string Format() { + StringBuilder sb = new StringBuilder(); + if (mItems.Count > 0) { + sb.AppendFormat("File {0}:\r\n", FileName); + } + foreach (FileLoadItem item in mItems) { + if (item.Line != FileLoadItem.NO_LINE && item.Column != FileLoadItem.NO_COLUMN) { + sb.AppendFormat(" Line {0}.{1}: {2}: {3}\r\n", item.Line, item.Column, + item.MsgType.ToString().ToLower(), item.Message); + } else if (item.Line != FileLoadItem.NO_LINE) { + sb.AppendFormat(" Line {0}: {1}: {2}\r\n", item.Line, + item.MsgType.ToString().ToLower(), item.Message); + } else { + // Capitalized form looks nicer here. + sb.AppendFormat(" {0}: {1}\r\n", item.MsgType.ToString(), item.Message); + } + } + return sb.ToString(); + } + + public override string ToString() { + return "FileLoadReport: count=" + mItems.Count + " hasWarn=" + + HasWarnings + " hasErr=" + HasErrors; + } + } +} diff --git a/CommonUtil/FileUtil.cs b/CommonUtil/FileUtil.cs new file mode 100644 index 0000000..3cb0289 --- /dev/null +++ b/CommonUtil/FileUtil.cs @@ -0,0 +1,141 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.IO; +using System.Text; + +namespace CommonUtil { + public static class FileUtil { + /// + /// Compares the contents of a file against a byte array. + /// + /// Expected data values. + /// Pathname of file to inspect. + /// Offset of first mismatch. If this is equal to expected.Length + /// or File(pathName).Length, then the contents were equal but one source stopped + /// early. The badFileVal parameter will be zero. + /// Mismatched value, from the file. + /// True if the contents match, false if not. + public static bool CompareBinaryFile(byte[] expected, string pathName, + out int badOffset, out byte badFileVal) { + badOffset = -1; + badFileVal = 0; + + int chunkOffset = 0; + byte[] buffer = new byte[65536]; + using (FileStream fs = File.Open(pathName, FileMode.Open, FileAccess.Read)) { + //if (fs.Length != expected.Length) { + // return false; + //} + int fileRemain = (int)fs.Length; + + while (fileRemain != 0) { + int toRead = Math.Min(buffer.Length, fileRemain); + int actual = fs.Read(buffer, 0, toRead); + if (actual != toRead) { + // File I/O problem; unexpected. + return false; + } + + for (int i = 0; i < toRead; i++) { + if (chunkOffset + i >= expected.Length) { + // File on disk was too long. + Debug.Assert(fs.Length > expected.Length); + badOffset = chunkOffset + i; + return false; + } + if (expected[chunkOffset + i] != buffer[i]) { + badOffset = chunkOffset + i; + badFileVal = buffer[i]; + return false; + } + } + + fileRemain -= toRead; + chunkOffset += toRead; + } + + if (fs.Length != expected.Length) { + Debug.Assert(fs.Length < expected.Length); + badOffset = (int) fs.Length; + return false; + } + } + + return true; + } + + /// + /// Compares two text files line-by-line. Ignores line termination characters. + /// Assumes UTF-8 encoding. + /// + /// Full path of first file. + /// Full path of second file. + /// Line number of first differing line. + /// Differing line from first file. + /// Differing line from second file. + /// True if the files are equal. + public static bool CompareTextFiles(string pathName1, string pathName2, + out int firstDiffLine, out string line1, out string line2) { + int line = 0; + using (StreamReader sr1 = new StreamReader(pathName1, Encoding.UTF8)) { + using (StreamReader sr2 = new StreamReader(pathName2, Encoding.UTF8)) { + while (true) { + // ReadLine strips the EOL char(s) + string str1 = sr1.ReadLine(); + string str2 = sr2.ReadLine(); + line++; + if (str1 != str2) { + firstDiffLine = line; + line1 = str1; + line2 = str2; + return false; + } + if (str1 == null) { + Debug.Assert(str2 == null); + break; + } + } + } + } + + firstDiffLine = -1; + line1 = line2 = null; + return true; + } + + /// + /// Determines whether the destination file is missing or older than the source file. + /// This can be used do decide whether it's necessary to copy srcFile over. + /// + /// File of interest. + /// File to compare dates with. + /// True if dstFile is missing or older than srcFile. + public static bool FileMissingOrOlder(string dstFile, string srcFile) { + FileInfo fid = new FileInfo(dstFile); + if (!fid.Exists) { + return true; // not there + } + FileInfo fis = new FileInfo(srcFile); + if (!fis.Exists) { + return false; // nothing to compare against + } + + return (fid.LastWriteTimeUtc < fis.LastWriteTimeUtc); + } + } +} diff --git a/CommonUtil/Misc.cs b/CommonUtil/Misc.cs new file mode 100644 index 0000000..71625fe --- /dev/null +++ b/CommonUtil/Misc.cs @@ -0,0 +1,32 @@ +/* + * Copyright 2018 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.Linq; + +namespace CommonUtil { + public static class Misc { + // Given a type, dump all namespaces found in the same assembly. + // https://stackoverflow.com/a/1549216/294248 + public static void DumpNamespacesInAssembly(Type type) { + Console.WriteLine("Assembly: " + type.Assembly.Location); + Type[] typeList = type.Assembly.GetTypes(); + var namespaces = typeList.Select(t => t.Namespace).Distinct(); + foreach (string ns in namespaces) { + Console.WriteLine(" " + ns); + } + } + } +} diff --git a/CommonUtil/Properties/Resources.Designer.cs b/CommonUtil/Properties/Resources.Designer.cs new file mode 100644 index 0000000..b61cbb7 --- /dev/null +++ b/CommonUtil/Properties/Resources.Designer.cs @@ -0,0 +1,108 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CommonUtil.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CommonUtil.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to File not found. + /// + public static string ERR_FILE_NOT_FOUND { + get { + return ResourceManager.GetString("ERR_FILE_NOT_FOUND", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid address. + /// + public static string ERR_INVALID_ADDRESS { + get { + return ResourceManager.GetString("ERR_INVALID_ADDRESS", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid numeric constant. + /// + public static string ERR_INVALID_NUMERIC_CONSTANT { + get { + return ResourceManager.GetString("ERR_INVALID_NUMERIC_CONSTANT", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Syntax error. + /// + public static string ERR_SYNTAX { + get { + return ResourceManager.GetString("ERR_SYNTAX", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to -.-.-. + /// + public static string NO_VERSION { + get { + return ResourceManager.GetString("NO_VERSION", resourceCulture); + } + } + } +} diff --git a/CommonUtil/Properties/Resources.resx b/CommonUtil/Properties/Resources.resx new file mode 100644 index 0000000..88d70bb --- /dev/null +++ b/CommonUtil/Properties/Resources.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + File not found + + + Invalid address + + + Invalid numeric constant + + + Syntax error + + + -.-.- + + \ No newline at end of file diff --git a/CommonUtil/RangeSet.cs b/CommonUtil/RangeSet.cs new file mode 100644 index 0000000..e13014d --- /dev/null +++ b/CommonUtil/RangeSet.cs @@ -0,0 +1,426 @@ +/* + * Copyright 2018 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; +using System.Collections.Generic; +using System.Diagnostics; + +namespace CommonUtil { + /// + /// Compact representation of a set of integers that tend to be adjacent. + /// + /// The default enumeration is a series of integers, not a series of ranges. Use + /// RangeListIterator to get the latter. + /// + /// Most operations operate in log(N) time, where N is the number of + /// regions. + /// + public class RangeSet : IEnumerable { + /// + /// List of ranges, in sorted order. + /// + private List mRangeList = new List(); + + /// + /// Number of values in the set. + /// + public int Count { get; private set; } + + /// + /// For unit tests: return the number of Range elements in the list. + /// + public int DebugRangeCount { get { return mRangeList.Count; } } + + /// + /// Represents a contiguous range of values. + /// + public struct Range { + /// + /// Lowest value (inclusive). + /// + public int Low { get; set; } + + /// + /// Highest value (inclusive). + /// + public int High { get; set; } + + public Range(int low, int high) { + Debug.Assert(low <= high); + Low = low; + High = high; + } + + /// + /// Returns true if the specified value falls in this range. + /// + public bool Contains(int val) { + return (val >= Low && val <= High); + } + } + + /// + /// Iterator definition. + /// + private class RangeSetIterator : IEnumerator { + /// + /// The RangeSet we're iterating over. + /// + private RangeSet mSet; + + // Index of current Range element in mSet.mRangeList. + private int mListIndex = -1; + + // Current range, extracted from mRangeList. + private Range mCurrentRange; + + // Current value in mCurrentRange. + private int mCurrentVal; + + + /// + /// Constructor. + /// + /// RangeSet to iterate over. + public RangeSetIterator(RangeSet set) { + mSet = set; + Reset(); + } + + // IEnumerator: current element + public object Current { + get { + if (mListIndex < 0) { + // not started + return null; + } + return mCurrentVal; + } + } + + /// + /// Puts the next range in the list in mCurrentRange. + /// + /// True on success, false if we reached the end of the list. + private bool GetNextRange() { + mListIndex++; // increments to 0 on first invocation + if (mListIndex == mSet.mRangeList.Count) { + // no more ranges + return false; + } + + mCurrentRange = mSet.mRangeList[mListIndex]; + mCurrentVal = mCurrentRange.Low; + return true; + } + + // IEnumerator: move to the next element, returning false if there isn't one + public bool MoveNext() { + if (mListIndex < 0) { + // just started + return GetNextRange(); + } else { + // iterating within range object + mCurrentVal++; + if (mCurrentVal > mCurrentRange.High) { + // finished with this one, move on to the next + return GetNextRange(); + } else { + return true; + } + } + } + + // IEnumerator: reset state + public void Reset() { + mListIndex = -1; + } + } + + + /// + /// General constructor. Creates an empty set. + /// + public RangeSet() { + Count = 0; + } + + /// + /// Constructs set from an iterator. + /// + /// Iterator that generates a set of integers in ascending order. + public RangeSet(IEnumerator iter) : this() { + if (!iter.MoveNext()) { + return; + } + int first = (int) iter.Current; + Count++; + Range curRange = new Range(first, first); + + while (iter.MoveNext()) { + int val = (int) iter.Current; + Count++; + if (val == curRange.High + 1) { + // Add to current range. + curRange.High = val; + } else { + // Not contiguous, create new range. + mRangeList.Add(curRange); + curRange = new Range(val, val); + } + } + + mRangeList.Add(curRange); + } + + /// + /// Returns an enumerator that iterates through the range list, returning Range objects. + /// + public IEnumerator RangeListIterator { + get { return mRangeList.GetEnumerator(); } + } + + /// + /// Removes all values from the set. + /// + public void Clear() { + mRangeList.Clear(); + Count = 0; + } + + // IEnumerable: get an enumerator instance that returns integer values + public IEnumerator GetEnumerator() { + return new RangeSetIterator(this); + } + + // IEnumerable + IEnumerator IEnumerable.GetEnumerator() { + return (IEnumerator)GetEnumerator(); + } + + /// + /// Finds the range that contains "val", or an appropriate place in the list to + /// insert a new range. + /// + /// Value to find. + /// The index of the matching element, or a negative value indicating + /// the index to insert at. 2C doesn't support negative 0, so the insertion + /// index will be incremented before negation. + private int FindValue(int val) { + int low = 0; + int high = mRangeList.Count - 1; + while (low <= high) { + int mid = (low + high) / 2; + Range midRange = mRangeList[mid]; + + if (midRange.Contains(val)) { + // found it + return mid; + } else if (val < midRange.Low) { + // too big, move the high end in + high = mid - 1; + } else if (val > midRange.High) { + // too small, move the low end in + low = mid + 1; + } else { + // WTF... list not sorted? + throw new Exception("Bad binary search"); + } + } + + // Not found, insert before "low". + return -(low + 1); + } + + /// + /// Determines whether val is a member of the set. + /// + /// Value to check. + /// True if the value is a member of the set. + public bool Contains(int val) { + return (FindValue(val) >= 0); + } + + /// + /// Adds a value to the set. If the value is already present, nothing changes. + /// + /// Value to add. + public void Add(int val) { + int listIndex = FindValue(val); + if (listIndex >= 0) { + // Already present in set. + return; + } + Count++; + + if (mRangeList.Count == 0) { + // Empty list, skip the gymnastics. + mRangeList.Add(new Range(val, val)); + return; + } + + // Negate and decrement to get insertion index. This value may == Count if + // the value is higher than all current members. + listIndex = -listIndex - 1; + + if (listIndex > 0 && mRangeList[listIndex - 1].High == val - 1) { + // Expand prior range. Check to see if it blends into next. + if (listIndex < mRangeList.Count && mRangeList[listIndex].Low == val + 1) { + // Combine ranges. + Range prior = mRangeList[listIndex - 1]; + Range next = mRangeList[listIndex]; + Debug.Assert(prior.High + 2 == next.Low); + prior.High = next.High; + mRangeList[listIndex - 1] = prior; + mRangeList.RemoveAt(listIndex); + } else { + // Nope, just expand the prior range. + Range prior = mRangeList[listIndex - 1]; + Debug.Assert(prior.High == val - 1); + prior.High = val; + mRangeList[listIndex - 1] = prior; + } + } else if (listIndex < mRangeList.Count && mRangeList[listIndex].Low == val + 1) { + // Expand next range. + Range next = mRangeList[listIndex]; + Debug.Assert(next.Low == val + 1); + next.Low = val; + mRangeList[listIndex] = next; + } else { + // Add a new single-entry element. + mRangeList.Insert(listIndex, new Range(val, val)); + } + } + + /// + /// Adds a range of contiguous values to the set. + /// + /// Lowest value (inclusive). + /// Highest value (inclusive). + public void AddRange(int low, int high) { + // There's probably some very efficient way to do this. Keeping it simple for now. + for (int i = low; i <= high; i++) { + Add(i); + } + } + + /// + /// Removes a value from the set. If the value is not present, nothing changes. + /// + /// Value to remove. + public void Remove(int val) { + int listIndex = FindValue(val); + if (listIndex < 0) { + // not found + return; + } + + Count--; + + Range rng = mRangeList[listIndex]; + if (rng.Low == val && rng.High == val) { + // Single-value range. Remove. + mRangeList.RemoveAt(listIndex); + } else if (rng.Low == val) { + // We're at the low end, reduce range. + rng.Low = val + 1; + mRangeList[listIndex] = rng; + } else if (rng.High == val) { + // We're at the high end, reduce range. + rng.High = val - 1; + mRangeList[listIndex] = rng; + } else { + // We're in the middle, split the range. + Range next = new Range(val + 1, rng.High); + rng.High = val - 1; + mRangeList[listIndex] = rng; + mRangeList.Insert(listIndex + 1, next); + } + } + + + /// + /// Internal test function. + /// + private static bool CheckRangeSet(RangeSet set, int expectedRanges, int[] expected) { + if (set.DebugRangeCount != expectedRanges) { + Debug.WriteLine("Expected " + expectedRanges + " ranges, got " + + set.DebugRangeCount); + return false; + } + + // Compare actual vs. expected. If we have more actual than expected we'll + // throw on the array access. + int expIndex = 0; + foreach (int val in set) { + if (val != expected[expIndex]) { + Debug.WriteLine("Expected " + expected[expIndex] + ", got " + val); + return false; + } + expIndex++; + } + + // See if we have more expected than actual. + if (expIndex != expected.Length) { + Debug.WriteLine("Expected " + expected.Length + " elements, found " + expIndex); + return false; + } + + // The count is maintained separately, so check it. + if (set.Count != expected.Length) { + Debug.WriteLine("Expected Count=" + expected.Length + ", got " + set.Count); + return false; + } + return true; + } + + /// + /// Executes unit tests. + /// + /// True if all goes well. + public static bool Test() { + bool result = true; + + RangeSet one = new RangeSet(); + one.Add(7); + one.Add(5); + one.Add(3); + one.Add(9); + one.Add(7); + one.Add(8); + one.Add(2); + one.Add(4); + result &= CheckRangeSet(one, 2, new int[] { 2, 3, 4, 5, 7, 8, 9 }); + + one.Remove(2); + one.Remove(9); + one.Remove(4); + result &= CheckRangeSet(one, 3, new int[] { 3, 5, 7, 8 }); + + one.Clear(); + one.AddRange(10, 15); + result &= CheckRangeSet(one, 1, new int[] { 10, 11, 12, 13, 14, 15 }); + + one.Add(-1); + one.Add(0); + one.Add(-2); + result &= CheckRangeSet(one, 2, new int[] { -2, -1, 0, 10, 11, 12, 13, 14, 15 }); + + Debug.WriteLine("RangeSet: test complete (ok=" + result + ")"); + return result; + } + } +} diff --git a/CommonUtil/RawData.cs b/CommonUtil/RawData.cs new file mode 100644 index 0000000..55a4298 --- /dev/null +++ b/CommonUtil/RawData.cs @@ -0,0 +1,63 @@ +/* + * Copyright 2018 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.Diagnostics; + +namespace CommonUtil { + public class RawData { + /// + /// Extracts an integer from the data stream. + /// + /// Raw data stream. + /// Start offset. + /// Word width, which may be 1-4 bytes. + /// True if word is in big-endian order. + /// Value found. + public static int GetWord(byte[] data, int offset, int width, bool isBigEndian) { + if (width < 1 || width > 4 || offset + width > data.Length) { + throw new ArgumentOutOfRangeException("GetWord(offset=" + offset + " width=" + + width + "), data.Length=" + data.Length); + } + if (isBigEndian) { + switch (width) { + case 1: + return data[offset]; + case 2: + return (data[offset] << 8) | data[offset + 1]; + case 3: + return (data[offset] << 16) | (data[offset + 1] << 8) | data[offset + 2]; + case 4: + return (data[offset] << 24) | (data[offset + 1] << 16) | + (data[offset + 2] << 8) | data[offset + 3]; + } + } else { + switch (width) { + case 1: + return data[offset]; + case 2: + return data[offset] | (data[offset + 1] << 8); + case 3: + return data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16); + case 4: + return data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | + data[offset + 3] << 24; + } + } + + throw new Exception("GetWord(): should not be here"); + } + } +} diff --git a/CommonUtil/ShellCommand.cs b/CommonUtil/ShellCommand.cs new file mode 100644 index 0000000..234f8fe --- /dev/null +++ b/CommonUtil/ShellCommand.cs @@ -0,0 +1,222 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Runtime.InteropServices; + +namespace CommonUtil { + /// + /// Execute a shell command and return stdout/stderr. + /// + /// Returning stdout/stderr separately loses the interleave, but it's unclear whether + /// that gets lost anyway with output buffering and the asynchronous I/O facility. + /// + public class ShellCommand { + // These were handy: + // https://stackoverflow.com/a/32872174/294248 + // https://stackoverflow.com/a/18616369/294248 + // https://stackoverflow.com/a/7334029/294248 + // https://stackoverflow.com/a/5187715/294248 + + private const int CMD_TIMEOUT_MS = 10000; // 10 sec + private bool USE_CMD_EXE = false; + + /// + /// Filename of shell command to execute. + /// + public string CommandFileName { get; private set; } + + /// + /// Arguments to pass to command. Individual arguments are separated by spaces. + /// If an argument may contain spaces (e.g. it's a filename), surround it with + /// double quotes ("). + /// + public string Arguments { get; private set; } + + /// + /// Working directory for command. The directory will be changed for the + /// command only. + /// + public string WorkDirectory { get; private set; } + + public Dictionary EnvVars { get; private set; } + + /// + /// The full command line, for display purposes. This is just CommandFileName + Arguments + /// unless some funny business is going on under the hood. + /// + public string FullCommandLine { get; private set; } + + /// + /// Output from stdout. + /// + public string Stdout { get; private set; } + + /// + /// Output from stderr. + /// + public string Stderr { get; private set; } + + /// + /// Command exit code. Will be 0 on success. + /// + public int ExitCode { get; private set; } + + /// + /// Buffers for gathering stdout/stderr. + /// + private StringBuilder mStdout, mStderr; + + + /// + /// Constructor. + /// + /// Filename of command to execute. + /// Command arguments, separated by spaces. Surround args with + /// embedded spaces with double quotes. Pass empty string if no args. + /// Working directory for command. Pass an empty string if you + /// want to use the default. + /// Dictionary of values to set in the shell environment. + public ShellCommand(string commandFileName, string arguments, string workDir, + Dictionary env) { + Debug.Assert(commandFileName != null); + Debug.Assert(arguments != null); + Debug.Assert(workDir != null); + + CommandFileName = commandFileName; + Arguments = arguments; + WorkDirectory = workDir; + EnvVars = env; + + ExitCode = -100; + + mStdout = new StringBuilder(); + mStderr = new StringBuilder(); + } + + /// + /// Execute the command. + /// + /// Process exit code. 0 on success. + public void Execute() { + // Works for full paths to command, but not for shell stuff like "dir". + FileInfo fi = new FileInfo(CommandFileName); + if (!fi.Exists) { + Debug.WriteLine("Warning: file '" + CommandFileName + "' does not exist"); + } + + ProcessStartInfo psi = new ProcessStartInfo(); + if (USE_CMD_EXE) { + // Run inside cmd.exe on Windows. + psi.FileName = "cmd.exe"; + psi.Arguments = "/C " + CommandFileName + + (string.IsNullOrEmpty(Arguments) ? "" : " " + Arguments); + } else { + psi.FileName = CommandFileName; + psi.Arguments = Arguments; + } + FullCommandLine = psi.FileName + " " + psi.Arguments; + psi.CreateNoWindow = true; + psi.RedirectStandardInput = true; + psi.RedirectStandardOutput = true; + psi.RedirectStandardError = true; + psi.UseShellExecute = false; // required for stdin/stdout redirect + if (!string.IsNullOrEmpty(WorkDirectory)) { + psi.WorkingDirectory = WorkDirectory; + } + + if (EnvVars != null) { + foreach (KeyValuePair kvp in EnvVars) { + Debug.WriteLine("ENV: " + kvp.Key + "=" + kvp.Value); + psi.Environment.Add(kvp); + } + } + + try { + using (Process process = Process.Start(psi)) { + process.OutputDataReceived += (sendingProcess, outLine) => + mStdout.AppendLine(outLine.Data); + process.ErrorDataReceived += (sendingProcess, errLine) => + mStderr.AppendLine(errLine.Data); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + // Close stdin so interactive programs don't stall. + process.StandardInput.Close(); + + // I'm calling with a (fairly long) timeout, just in case. + process.WaitForExit(CMD_TIMEOUT_MS); + if (!process.HasExited) { + Debug.WriteLine("Process stalled, killing"); + process.Kill(); + } + + // WaitForExit(timeout) can return before the async stdout/stderr stuff + // has completed. Calling it without a timeout ensures correct behavior. + process.WaitForExit(); + + // This will be zero on success. I've seen 1 when the command wasn't + // found, and -1 when the process was killed. + ExitCode = process.ExitCode; + } + } catch (Exception ex) { + // This can happen if the command doesn't exist. + ExitCode = -2000; + Stdout = string.Empty; + Stderr = "Failed to execute command: " + FullCommandLine + "\r\n" + ex.ToString(); + return; + } + + Stdout = mStdout.ToString(); + Stderr = mStderr.ToString(); + } + + /// + /// Opens a tab in the system web browser for the specified URL. + /// + /// NOTE: on Windows 10, as of 2018/09/02, this loses the anchor (the "#thing" at the + /// end of the URL). Chasing through various stackoverflow posts, it appears the + /// only way around this is to invoke the specific browser (which you dredge out of + /// the Registry). + /// + public static void OpenUrl(string url) { + // See https://stackoverflow.com/a/43232486/294248 + // The idea is to see if Start() will just do it, and if it doesn't then fall + // back on something platform-specific. I don't know if this is actually + // necessary -- I suspect Mono will do the right thing. + try { + Process.Start(url); + } catch { + // hack because of this: https://github.com/dotnet/corefx/issues/10361 + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + url = url.Replace("&", "^&"); + Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") { + CreateNoWindow = true + }); + } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { + Process.Start("xdg-open", url); + } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { + Process.Start("open", url); + } else { + throw; + } + } + } + } +} diff --git a/CommonUtil/TaskTimer.cs b/CommonUtil/TaskTimer.cs new file mode 100644 index 0000000..765dce0 --- /dev/null +++ b/CommonUtil/TaskTimer.cs @@ -0,0 +1,199 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Text; + +namespace CommonUtil { + /// + /// Collects timestamps from a series of events. Events may be nested, but may not overlap. + /// A summary of task durations can be written to a log. + /// + public class TaskTimer { + // TODO(maybe): create a start/end pair that works with a "using" directive to ensure + // that a given item is always closed. + + /// + /// Timed task info. + /// + private class TimedItem { + public string mTag; + public int mIndentLevel; + public DateTime mStartWhen; + public DateTime mEndWhen; + + public TimedItem(string tag, int indentLevel) { + mTag = tag; + mIndentLevel = indentLevel; + mStartWhen = DateTime.Now; + } + } + + /// + /// List of items. They are ordered by when the tasks ended. + /// + private List mItems = new List(); + + /// + /// Indentation level. Cosmetic. + /// + private int mIndentLevel = 0; + + /// + /// Place where next record is to be inserted. + /// + /// We keep inserting records ahead of whatever we inserted last, only advancing + /// the insertion point when we close a record. Essentially a stack that moves + /// forward when you pop() instead of removing the element. This lets us handle + /// nested tasks correctly. + /// + private int mInsertPoint = 0; + + + /// + /// Resets object to initial state. + /// + public void Clear() { + mItems.Clear(); + mIndentLevel = mInsertPoint = 0; + } + + /// + /// Adds a start record for a task. + /// + /// Task tag. + public void StartTask(string tag) { + TimedItem ti = new TimedItem(tag, mIndentLevel); + mItems.Insert(mInsertPoint, ti); + mIndentLevel++; + } + + /// + /// Closes out a record. The tag must match the most recently started task. + /// + /// Task tag. + public void EndTask(string tag) { + TimedItem lastItem = mItems[mInsertPoint]; + if (lastItem.mTag != tag) { + Debug.WriteLine("ERROR: tag mismatch: " + tag + " vs. " + lastItem.mTag); + Debug.Assert(false); + return; + } + + lastItem.mEndWhen = DateTime.Now; + mIndentLevel--; + mInsertPoint++; + Debug.Assert(mIndentLevel >= 0); + } + + /// + /// Prints the timing data into a log object. + /// + /// Output destination. + /// Header message. + public void DumpTimes(string msg, DebugLog log) { + if (mItems.Count == 0) { + return; + } + if (!string.IsNullOrEmpty(msg)) { + log.LogI(msg); + } + StringBuilder sb = new StringBuilder(); + int lastIndent = 0; + foreach (TimedItem ti in mItems) { + sb.Clear(); + FormatItem(ti, ref lastIndent, sb); + log.LogI(sb.ToString()); + } + + //DateTime firstStart = mItems[0].mStartWhen; + //DateTime lastEnd = mItems[mItems.Count - 1].mEndWhen; + //log.LogI(" Total: " + (lastEnd - firstStart).TotalMilliseconds + " ms"); + } + + /// + /// Prints the timing data into the debug log. + /// + /// Header message. + public void DumpTimes(string msg) { + if (mItems.Count == 0) { + return; + } + if (!string.IsNullOrEmpty(msg)) { + Debug.WriteLine(msg); + } + StringBuilder sb = new StringBuilder(); + int lastIndent = 0; + foreach (TimedItem ti in mItems) { + sb.Clear(); + FormatItem(ti, ref lastIndent, sb); + Debug.WriteLine(sb.ToString()); + } + } + + /// + /// Prints the timing data into a string with newlines. + /// + /// Output destination. + /// Header message. + public string DumpToString(string msg) { + if (mItems.Count == 0) { + return msg; + } + StringBuilder sb = new StringBuilder(); +#if DEBUG + sb.Append("[NOTE: debug build -- assertions and extra checks are enabled]\r\n\r\n"); +#endif + if (!string.IsNullOrEmpty(msg)) { + sb.Append(msg); + sb.Append("\r\n\r\n"); + } + int lastIndent = 0; + foreach (TimedItem ti in mItems) { + FormatItem(ti, ref lastIndent, sb); + sb.Append("\r\n"); + } + + return sb.ToString(); + } + + + /// + /// Formats the specified item, appending it to the StringBuilder. + /// + /// Item to format. + /// Previous indentation level. + /// StringBuilder to append to. + private void FormatItem(TimedItem ti, ref int lastIndent, StringBuilder sb) { + for (int i = 0; i <= ti.mIndentLevel - 1; i++) { + sb.Append("| "); + } + if (lastIndent < ti.mIndentLevel) { + //sb.Append("/-"); + sb.Append("/ "); + } else /*if (lastIndent == ti.mIndentLevel)*/ { + sb.Append("| "); + } + sb.Append(ti.mTag); + sb.Append(": "); + sb.Append((ti.mEndWhen - ti.mStartWhen).TotalMilliseconds.ToString()); + sb.Append(" ms"); + + lastIndent = ti.mIndentLevel; + } + } +} diff --git a/CommonUtil/TextUtil.cs b/CommonUtil/TextUtil.cs new file mode 100644 index 0000000..3473343 --- /dev/null +++ b/CommonUtil/TextUtil.cs @@ -0,0 +1,241 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace CommonUtil { + /// + /// Text utility functions. + /// + public static class TextUtil { + // 100 spaces, useful when padding things out. + private const string SPACES = " " + + " "; + private static readonly char[] CSV_ESCAPE_CHARS = { ',', '"' }; + private const string NonPrintableAsciiPattern = @"[^\u0020-\u007e]"; + private static Regex sNonPrintableAsciiRegex = new Regex(NonPrintableAsciiPattern); + + /// + /// Converts a string to an ASCII-only string, replacing iso-latin characters + /// with their ASCII equivalents. This may change the length of the string. + /// + /// For example, "¿Dónde está Über bären?" becomes "Donde esta Uber baren?". + /// + /// + /// Converted string. + public static string LatinToAscii(string inString) { + // https://stackoverflow.com/questions/140422/ + var newStringBuilder = new StringBuilder(); + newStringBuilder.Append(inString.Normalize(NormalizationForm.FormKD) + .Where(x => x < 128) + .ToArray()); + return newStringBuilder.ToString(); + + // Alternatively? + // System.Text.Encoding.ASCII.GetString( + // System.Text.Encoding.GetEncoding(1251).GetBytes(text)) + } + + /// + /// Returns true if the value is valid high- or low-ASCII. + /// + /// Value to test. + /// True if val is valid ASCII. + public static bool IsHiLoAscii(int val) { + return (val >= 0x20 && val < 0x7f) || (val >= 0xa0 && val < 0xff); + } + + /// + /// Determines whether the character is printable ASCII. + /// + /// Character to evaluate. + /// True if the character is printable ASCII. + public static bool IsPrintableAscii(char ch) { + return ch >= 0x20 && ch < 0x7f; + } + + /// + /// Determines whether the string has nothing but printable ASCII characters in it. + /// + /// String to evaluate. + /// True if all characters are printable ASCII. + public static bool IsPrintableAscii(string str) { + // Linq version: return str.Any(c => c < 0x20 || c > 0x7e); + + MatchCollection matches = sNonPrintableAsciiRegex.Matches(str); + return matches.Count == 0; + } + + /// + /// Converts high-ASCII bytes to a string. + /// + /// Array of bytes with ASCII data. + /// Start offset. + /// String length. + /// Converted string. + public static string HighAsciiToString(byte[] data, int offset, int length) { + StringBuilder sb = new StringBuilder(length); + for (int i = offset; i < offset + length; i++) { + sb.Append((char)(data[i] & 0x7f)); + } + return sb.ToString(); + } + + /// + /// Trims whitespace off the end of a StringBuilder. + /// + /// StringBuilder reference. + public static void TrimEnd(StringBuilder sb) { + const string WSPC = " \t\r\n"; + int len = sb.Length; + while (WSPC.IndexOf(sb[len - 1]) >= 0) { + len--; + } + sb.Length = len; + } + + /// + /// Replaces all occurrences of a string with another string, but only if they appear + /// outside quoted text. Useful for replacing structural bits of a JSON string without + /// messing with the quoted items. Assumes that quoted quotes (backslash-quote) only + /// appear inside quoted text. + /// + /// + /// + /// + public static string NonQuoteReplace(string inStr, string findStr, string repStr) { + // There's probably a better way to do this... + StringBuilder sb = new StringBuilder(inStr.Length + inStr.Length / 20); + int cmpLen = findStr.Length; + bool findStrQuote = findStr.Contains('\"'); // find/rep str switches to in-quote + Debug.Assert(findStrQuote == repStr.Contains('\"')); + + bool inQuote = false; + for (int i = 0; i < inStr.Length; i++) { + char ch = inStr[i]; + if (inQuote) { + // Check to see if the double-quote is quoted. It's safe to back up + // one because we don't start in-quote. + if (ch == '\"' && inStr[i-1] != '\\') { + inQuote = false; + } else { + // in quoted text, keep going + } + sb.Append(ch); + } else { + if (string.Compare(inStr, i, findStr, 0, cmpLen) == 0) { + sb.Append(repStr); + i += cmpLen - 1; + inQuote = findStrQuote; + } else { + if (ch == '"') { + // There are no quoted-quotes outside of quotes, so we don't need + // to check for a '\'. + inQuote = true; + } + sb.Append(ch); + } + } + } + + return sb.ToString(); + } + + /// + /// Pads a string with trailing spaces so that the total length of the line, including + /// previous contents, is the length specified. One trailing space will be added even + /// if the string's length is >= toLen. + /// + /// You must begin with an empty StringBuilder at the start of each line. + /// + /// StringBuilder to append to. + /// String to add. + /// Total line width to pad to. + public static void AppendPaddedString(StringBuilder sb, string str, int toLen) { + if (str == null) { + str = string.Empty; + } + int newLen = sb.Length + str.Length; + if (newLen >= toLen) { + sb.Append(str); + sb.Append(' '); + } else { + sb.Append(str); + // would be nice to avoid this allocation/copy + sb.Append(SPACES.Substring(0, toLen - newLen)); + } + } + + /// + /// Escapes a string for CSV. + /// + /// String to process. + /// Escaped string, or an empty string if the input was null. + public static string EscapeCSV(string str) { + if (str == null) { + return string.Empty; + } + bool needQuote = (str.IndexOfAny(CSV_ESCAPE_CHARS) >= 0); + if (needQuote) { + return '"' + str.Replace("\"", "\"\"") + '"'; + } else { + return str; + } + } + + /// + /// Serializes an integer array into a string. + /// + /// Array to serialize. + /// Serialized data. + public static string SerializeIntArray(int[] values) { + StringBuilder sb = new StringBuilder(64); + sb.Append("int[]"); + for (int i = 0; i < values.Length; i++) { + sb.Append(','); + sb.Append(values[i]); + } + return sb.ToString(); + } + + /// + /// Deserializes an integer array from a string. + /// + /// + /// + public static int[] DeserializeIntArray(string cereal) { + string[] splitted = cereal.Split(','); + if (splitted.Length == 0) { + throw new Exception("Bad serialized int[]"); + } + if (splitted[0] != "int[]") { + throw new Exception("Bad serialized int[], started with " + splitted[0]); + } + int[] arr = new int[splitted.Length - 1]; + try { + for (int i = 1; i < splitted.Length; i++) { + arr[i - 1] = int.Parse(splitted[i]); + } + } catch (Exception ex) { + throw new Exception("Bad serialized int[]: " + ex.Message); + } + return arr; + } + } +} diff --git a/CommonUtil/TypedRangeSet.cs b/CommonUtil/TypedRangeSet.cs new file mode 100644 index 0000000..6380b09 --- /dev/null +++ b/CommonUtil/TypedRangeSet.cs @@ -0,0 +1,511 @@ +/* + * Copyright 2018 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; +using System.Collections.Generic; +using System.Diagnostics; + +namespace CommonUtil { + /// + /// Compact representation of a set of typed integers that tend to be adjacent. + /// We expect there to be relatively few different types of things. + /// + /// The default enumeration is a series of integers, not a series of ranges. Use + /// RangeListIterator to get the latter. + /// + /// Most operations operate in log(N) time, where N is the number of + /// regions. + /// + public class TypedRangeSet : IEnumerable { + /// + /// List of ranges, in sorted order. + /// + private List mRangeList = new List(); + + /// + /// Number of values in the set. + /// + public int Count { get; private set; } + + /// + /// Returns the number of Range elements in the list. + /// + public int RangeCount { get { return mRangeList.Count; } } + + /// + /// Represents a contiguous range of values. + /// + public struct TypedRange { + /// + /// Lowest value (inclusive). + /// + public int Low { get; set; } + + /// + /// Highest value (inclusive). + /// + public int High { get; set; } + + /// + /// Value type in this range. + /// + public int Type { get; set; } + + public TypedRange(int low, int high, int type) { + Debug.Assert(low <= high); + Low = low; + High = high; + Type = type; + } + + public bool Contains(int val) { + return (val >= Low && val <= High); + } + } + + /// + /// Value + type pair. Returned from foreach enumerator. + /// + public struct Tuple { + public int Value; + public int Type; + + public Tuple(int value, int type) { + Value = value; + Type = type; + } + + public static bool operator ==(Tuple a, Tuple b) { + return a.Value == b.Value && a.Type == b.Type; + } + public static bool operator !=(Tuple a, Tuple b) { + return !(a == b); + } + public override bool Equals(object obj) { + return obj is Tuple && this == (Tuple)obj; + } + public override int GetHashCode() { + return Value ^ Type; + } + public override string ToString() { + return Value + " (" + Type + ")"; + } + } + + /// + /// Iterator definition. + /// + private class TypedRangeSetIterator : IEnumerator { + /// + /// The TypedRangeSet we're iterating over. + /// + private TypedRangeSet mSet; + + // Index of current Range element in mSet.mRangeList. + private int mListIndex = -1; + + // Current range, extracted from mRangeList. + private TypedRange mCurrentRange; + + // Current value in mCurrentRange. + private int mCurrentVal; + + + /// + /// Constructor. + /// + /// TypedRangeSet to iterate over. + public TypedRangeSetIterator(TypedRangeSet set) { + mSet = set; + Reset(); + } + + // IEnumerator: current element + public object Current { + get { + if (mListIndex < 0) { + // not started + return null; + } + return new Tuple(mCurrentVal, mCurrentRange.Type); + } + } + + // IEnumerator + Tuple IEnumerator.Current { + get { + return (Tuple)Current; + } + } + + /// + /// Puts the next range in the list in mCurrentRange. + /// + /// True on success, false if we reached the end of the list. + private bool GetNextRange() { + mListIndex++; // increments to 0 on first invocation + if (mListIndex == mSet.mRangeList.Count) { + // no more ranges + return false; + } + + mCurrentRange = mSet.mRangeList[mListIndex]; + mCurrentVal = mCurrentRange.Low; + return true; + } + + // IEnumerator: move to the next element, returning false if there isn't one + public bool MoveNext() { + if (mListIndex < 0) { + // just started + return GetNextRange(); + } else { + // iterating within range object + mCurrentVal++; + if (mCurrentVal > mCurrentRange.High) { + // finished with this one, move on to the next + return GetNextRange(); + } else { + return true; + } + } + } + + // IEnumerator: reset state + public void Reset() { + mListIndex = -1; + } + + // IEnumerator + public void Dispose() { + mSet = null; + } + } + + + /// + /// Constructor. Creates an empty set. + /// + public TypedRangeSet() { + Count = 0; + } + + /// + /// Returns an enumerator that iterates through the range list, returning Range objects. + /// + public IEnumerator RangeListIterator { + get { return mRangeList.GetEnumerator(); } + } + + /// + /// Removes all values from the set. + /// + public void Clear() { + mRangeList.Clear(); + Count = 0; + } + + // IEnumerable: get an enumerator instance that returns integer values + public IEnumerator GetEnumerator() { + return new TypedRangeSetIterator(this); + } + + // IEnumerable + IEnumerator IEnumerable.GetEnumerator() { + return (IEnumerator)GetEnumerator(); + } + + /// + /// Finds the range that contains "val", or an appropriate place in the list to + /// insert a new range. + /// + /// Value to find. + /// The index of the matching element, or a negative value indicating + /// the index to insert at. 2C doesn't support negative 0, so the insertion + /// index will be incremented before negation. + private int FindValue(int val) { + int low = 0; + int high = mRangeList.Count - 1; + while (low <= high) { + int mid = (low + high) / 2; + TypedRange midRange = mRangeList[mid]; + + if (midRange.Contains(val)) { + // found it + return mid; + } else if (val < midRange.Low) { + // too big, move the high end in + high = mid - 1; + } else if (val > midRange.High) { + // too small, move the low end in + low = mid + 1; + } else { + // WTF... list not sorted? + throw new Exception("Bad binary search"); + } + } + + // Not found, insert before "low". + return -(low + 1); + } + + /// + /// Determines whether val is a member of the set. + /// + /// Value to check. + /// True if the value is a member of the set. + public bool Contains(int val) { + return (FindValue(val) >= 0); + } + + /// + /// Gets the type of the specified value. + /// + /// Value to query. + /// Receives the type, or -1 if the value is not in the set. + /// True if the value is in the set. + public bool GetType(int val, out int type) { + int listIndex = FindValue(val); + if (listIndex >= 0) { + type = mRangeList[listIndex].Type; + return true; + } else { + type = -1; + return false; + } + } + + /// + /// Adds or changes a value to the set. If the value is already present and has + /// a matching type, nothing changes. + /// + /// Value to add. + /// Value's type. + public void Add(int val, int type) { + int listIndex = FindValue(val); + if (listIndex >= 0) { + // Value is present in set, check type. + if (mRangeList[listIndex].Type == type) { + // It's a match, do nothing. + return; + } + + // Wrong type. Remove previous entry, then fall through to add new. + Remove(val); + listIndex = FindValue(val); // get insertion point + } + Count++; + + if (mRangeList.Count == 0) { + // Empty list, skip the gymnastics. + mRangeList.Add(new TypedRange(val, val, type)); + return; + } + + // Negate and decrement to get insertion index. This value may == Count if + // the value is higher than all current members. + listIndex = -listIndex - 1; + + if (listIndex > 0 && mRangeList[listIndex - 1].High == val - 1 && + mRangeList[listIndex - 1].Type == type) { + // Expand prior range. Check to see if it blends into next as well. + if (listIndex < mRangeList.Count && mRangeList[listIndex].Low == val + 1 && + mRangeList[listIndex].Type == type) { + // Combine ranges. + TypedRange prior = mRangeList[listIndex - 1]; + TypedRange next = mRangeList[listIndex]; + Debug.Assert(prior.High + 2 == next.Low); + prior.High = next.High; + mRangeList[listIndex - 1] = prior; + mRangeList.RemoveAt(listIndex); + } else { + // Nope, just expand the prior range. + TypedRange prior = mRangeList[listIndex - 1]; + Debug.Assert(prior.High == val - 1); + prior.High = val; + mRangeList[listIndex - 1] = prior; + } + } else if (listIndex < mRangeList.Count && mRangeList[listIndex].Low == val + 1 && + mRangeList[listIndex].Type == type) { + // Expand next range. + TypedRange next = mRangeList[listIndex]; + Debug.Assert(next.Low == val + 1); + next.Low = val; + mRangeList[listIndex] = next; + } else { + // Nothing adjacent, add a new single-entry element. + mRangeList.Insert(listIndex, new TypedRange(val, val, type)); + } + } + + /// + /// Adds a range of contiguous values to the set. + /// + /// Lowest value (inclusive). + /// Highest value (inclusive). + /// Value type. + public void AddRange(int low, int high, int type) { + // There's probably some very efficient way to do this. Keeping it simple for now. + for (int i = low; i <= high; i++) { + Add(i, type); + } + } + + /// + /// Removes a value from the set. If the value is not present, nothing changes. + /// + /// Value to remove. + public void Remove(int val) { + int listIndex = FindValue(val); + if (listIndex < 0) { + // not found + return; + } + + Count--; + + TypedRange rng = mRangeList[listIndex]; + if (rng.Low == val && rng.High == val) { + // Single-value range. Remove. + mRangeList.RemoveAt(listIndex); + } else if (rng.Low == val) { + // We're at the low end, reduce range. + rng.Low = val + 1; + mRangeList[listIndex] = rng; + } else if (rng.High == val) { + // We're at the high end, reduce range. + rng.High = val - 1; + mRangeList[listIndex] = rng; + } else { + // We're in the middle, split the range. + TypedRange next = new TypedRange(val + 1, rng.High, rng.Type); + rng.High = val - 1; + mRangeList[listIndex] = rng; + mRangeList.Insert(listIndex + 1, next); + } + } + + + /// + /// Internal test function. + /// + private static bool CheckTypedRangeSet(TypedRangeSet set, int expectedRanges, + Tuple[] expected) { + if (set.RangeCount != expectedRanges) { + Debug.WriteLine("Expected " + expectedRanges + " ranges, got " + + set.RangeCount); + return false; + } + + // Compare actual vs. expected. If we have more actual than expected we'll + // throw on the array access. + int expIndex = 0; + foreach (TypedRangeSet.Tuple val in set) { + if (val != expected[expIndex]) { + Debug.WriteLine("Expected " + expected[expIndex] + ", got " + val); + return false; + } + expIndex++; + } + + // See if we have more expected than actual. + if (expIndex != expected.Length) { + Debug.WriteLine("Expected " + expected.Length + " elements, found " + expIndex); + return false; + } + + // The count is maintained separately, so check it. + if (set.Count != expected.Length) { + Debug.WriteLine("Expected Count=" + expected.Length + ", got " + set.Count); + return false; + } + return true; + } + + /// + /// Executes unit tests. + /// + /// True if all goes well. + public static bool Test() { + bool result = true; + + TypedRangeSet one = new TypedRangeSet(); + one.Add(7, 100); + one.Add(5, 100); + one.Add(3, 100); + one.Add(9, 100); + one.Add(7, 100); + one.Add(8, 100); + one.Add(2, 100); + one.Add(4, 100); + result &= CheckTypedRangeSet(one, 2, new Tuple[] { + new Tuple(2, 100), + new Tuple(3, 100), + new Tuple(4, 100), + new Tuple(5, 100), + new Tuple(7, 100), + new Tuple(8, 100), + new Tuple(9, 100) }); + + one.Remove(2); + one.Remove(9); + one.Remove(4); + result &= CheckTypedRangeSet(one, 3, new Tuple[] { + new Tuple(3, 100), + new Tuple(5, 100), + new Tuple(7, 100), + new Tuple(8, 100) }); + + one.Clear(); + one.Add(1, 200); + one.Add(3, 100); + one.Add(7, 100); + one.Add(5, 100); + one.Add(9, 100); + one.Add(6, 100); + one.Add(8, 100); + one.Add(6, 200); + one.Add(2, 200); + one.Add(4, 300); + one.Add(4, 100); + result &= CheckTypedRangeSet(one, 4, new Tuple[] { + new Tuple(1, 200), + new Tuple(2, 200), + new Tuple(3, 100), + new Tuple(4, 100), + new Tuple(5, 100), + new Tuple(6, 200), + new Tuple(7, 100), + new Tuple(8, 100), + new Tuple(9, 100) }); + + one.Add(6, 100); + result &= CheckTypedRangeSet(one, 2, new Tuple[] { + new Tuple(1, 200), + new Tuple(2, 200), + new Tuple(3, 100), + new Tuple(4, 100), + new Tuple(5, 100), + new Tuple(6, 100), + new Tuple(7, 100), + new Tuple(8, 100), + new Tuple(9, 100) }); + + Debug.WriteLine("TypedRangeSet: test complete (ok=" + result + ")"); + return result; + } + } +} diff --git a/CommonUtil/Version.cs b/CommonUtil/Version.cs new file mode 100644 index 0000000..4d9655d --- /dev/null +++ b/CommonUtil/Version.cs @@ -0,0 +1,192 @@ +/* + * Copyright 2018 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.Diagnostics; + +namespace CommonUtil { + /// + /// Version number container. Instances are immutable. + /// + /// See https://semver.org/ for explanation of system. + /// + public struct Version { + // Must be in ascending order, e.g. Alpha release comes before Beta. + public enum PreRelType { Dev, Alpha, Beta, Final }; + + /// + /// Major version number. + /// + public int Major { get; private set; } + + /// + /// Minor version number. + /// + public int Minor { get; private set; } + + /// + /// Bug fix release number. + /// + public int Patch { get; private set; } + + /// + /// Software grade, for pre-release versions. + /// + public PreRelType PreReleaseType { get; private set; } + + /// + /// Pre-release version. + /// + public int PreRelease { get; private set; } + + /// + /// Version instance to use when no version information is available. This will + /// always compare as less than a "real" version. + /// + public static readonly Version NO_VERSION = new Version(-1, -1, -1); + + /// + /// Shortcut for comparing vs. NO_VERSION. + /// + public bool IsValid { + get { + return this != NO_VERSION; + } + } + + + public Version(int major, int minor) : + this(major, minor, 0, PreRelType.Final, 0) { } + + public Version(int major, int minor, int patch) : + this(major, minor, patch, PreRelType.Final, 0) { } + + public Version(int major, int minor, int patch, PreRelType preRelType, int preRel) { + Debug.Assert(preRelType != PreRelType.Final || preRel == 0); + Major = major; + Minor = minor; + Patch = patch; + PreReleaseType = preRelType; + PreRelease = preRel; + } + + /// + /// Attempts to parse the argument into version components. + /// + /// Version string. + /// New Version object, or NO_VERSION on parsing failure. + public static Version Parse(string str) { + try { + int major, minor, patch; + major = minor = patch = 0; + + string[] parts = str.Split(new char[] { '.', '-' }); + major = int.Parse(parts[0]); + if (parts.Length > 1) { + minor = int.Parse(parts[1]); + } + if (parts.Length > 2) { + patch = int.Parse(parts[2]); + } + // parse the preRel thing someday + return new Version(major, minor, patch); + } catch (Exception ex) { + Debug.WriteLine("Version parse failed: '" + str + "': " + ex.Message); + return NO_VERSION; + } + } + + + public static bool operator ==(Version a, Version b) { + return a.Major == b.Major && a.Minor == b.Minor && a.Patch == b.Patch && + a.PreReleaseType == b.PreReleaseType && a.PreRelease == b.PreRelease; + } + public static bool operator !=(Version a, Version b) { + return !(a == b); + } + public override bool Equals(object obj) { + return obj is Version && this == (Version)obj; + } + public override int GetHashCode() { + return Major * 10000 + Minor * 1000 + Patch * 100 + + (int)PreReleaseType * 10 + PreRelease; + } + + public static bool operator <(Version a, Version b) { + if (a.Major != b.Major) { + return a.Major < b.Major; + } + if (a.Minor != b.Minor) { + return a.Minor < b.Minor; + } + if (a.Patch != b.Patch) { + return a.Patch < b.Patch; + } + if (a.PreReleaseType != b.PreReleaseType) { + return (int)a.PreReleaseType < (int)b.PreReleaseType; + } + if (a.PreRelease != b.PreRelease) { + return a.PreRelease < b.PreRelease; + } + Debug.Assert(a == b); + return false; + } + public static bool operator >(Version a, Version b) { + return b < a; + } + public static bool operator <=(Version a, Version b) { + return a == b || a < b; + } + public static bool operator >=(Version a, Version b) { + return a == b || a > b; + } + + public override string ToString() { + if (this == NO_VERSION) { + return Properties.Resources.NO_VERSION; + } else if (PreReleaseType == PreRelType.Final) { + return string.Format("{0}.{1}.{2}", Major, Minor, Patch); + } else { + return string.Format("{0}.{1}.{2}-{3}{4}", Major, Minor, Patch, + PreReleaseType.ToString().ToLower(), PreRelease); + } + } + + /// + /// Simple unit test. + /// + public static bool Test() { + bool ok = true; + + Version checkVers = new Version(1, 2, 3, PreRelType.Beta, 4); + Version sameVers = new Version(1, 2, 3, PreRelType.Beta, 4); + ok &= (checkVers == sameVers); + ok &= (checkVers <= sameVers); + ok &= (checkVers >= sameVers); + ok &= (!(checkVers < sameVers)); + ok &= (!(checkVers > sameVers)); + + ok &= (checkVers != new Version(1, 2, 3)); + ok &= (checkVers < new Version(1, 2, 3)); + ok &= (checkVers > new Version(1, 2, 3, PreRelType.Beta, 3)); + ok &= (checkVers < new Version(2, 0)); + ok &= (checkVers > new Version(1, 2, 2)); + ok &= (checkVers < new Version(1, 3, 1)); + + Debug.WriteLine("Version struct test complete (ok=" + ok + ")"); + return ok; + } + } +} diff --git a/CommonWinForms/CommonWinForms.csproj b/CommonWinForms/CommonWinForms.csproj new file mode 100644 index 0000000..ea54c28 --- /dev/null +++ b/CommonWinForms/CommonWinForms.csproj @@ -0,0 +1,49 @@ + + + + + Debug + AnyCPU + {08EC328D-078E-4236-B574-BE6B3FD85915} + Library + Properties + CommonWinForms + CommonWinForms + v4.6.1 + 512 + true + + + true + full + false + bin\Debug\ + TRACE;DEBUG;BUILD_FOR_WINDOWS + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;BUILD_FOR_WINDOWS + prompt + 4 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CommonWinForms/NativeMethods.cs b/CommonWinForms/NativeMethods.cs new file mode 100644 index 0000000..dc5847e --- /dev/null +++ b/CommonWinForms/NativeMethods.cs @@ -0,0 +1,85 @@ +/* + * Copyright 2018 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.Runtime.InteropServices; +using System.Windows.Forms; + +#if BUILD_FOR_WINDOWS // I don't know if other platforms will emulate the LVITEM stuff +namespace CommonWinForms { + // From https://stackoverflow.com/questions/1019388/ + + /// + /// Unpleasant hackery to make "select all" faster. With 500K items it takes about 24 + /// seconds to select everything individually, and there isn't a better way. With this + /// it only takes a few milliseconds. + /// + public class NativeMethods { + private const int LVM_FIRST = 0x1000; + private const int LVM_SETITEMSTATE = LVM_FIRST + 43; + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct LVITEM { + public int mask; + public int iItem; + public int iSubItem; + public int state; + public int stateMask; + [MarshalAs(UnmanagedType.LPTStr)] + public string pszText; + public int cchTextMax; + public int iImage; + public IntPtr lParam; + public int iIndent; + public int iGroupId; + public int cColumns; + public IntPtr puColumns; + }; + + [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)] + public static extern IntPtr SendMessageLVItem(IntPtr hWnd, int msg, int wParam, ref LVITEM lvi); + + /// + /// Select all rows on the given listview + /// + /// The listview whose items are to be selected + public static void SelectAllItems(ListView list) { + NativeMethods.SetItemState(list, -1, 2, 2); + } + + /// + /// Deselect all rows on the given listview + /// + /// The listview whose items are to be deselected + public static void DeselectAllItems(ListView list) { + NativeMethods.SetItemState(list, -1, 2, 0); + } + + /// + /// Set the item state on the given item + /// + /// The listview whose item's state is to be changed + /// The index of the item to be changed + /// Which bits of the value are to be set? + /// The value to be set + public static void SetItemState(ListView list, int itemIndex, int mask, int value) { + LVITEM lvItem = new LVITEM(); + lvItem.stateMask = mask; + lvItem.state = value; + SendMessageLVItem(list.Handle, LVM_SETITEMSTATE, itemIndex, ref lvItem); + } + } +} +#endif //BUILD_FOR_WINDOWS diff --git a/CommonWinForms/Properties/AssemblyInfo.cs b/CommonWinForms/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5306502 --- /dev/null +++ b/CommonWinForms/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CommonWinForms")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CommonWinForms")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("08ec328d-078e-4236-b574-be6b3fd85915")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CommonWinForms/WinFormsExtensions.cs b/CommonWinForms/WinFormsExtensions.cs new file mode 100644 index 0000000..9f54ea0 --- /dev/null +++ b/CommonWinForms/WinFormsExtensions.cs @@ -0,0 +1,121 @@ +/* + * Copyright 2018 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.Drawing; +using System.Windows.Forms; + +namespace CommonWinForms { + /// + /// Overload RichTextBox.AppendText() with a version that takes a color as an argument. + /// + /// From https://stackoverflow.com/a/1926822/294248 + /// + public static class RichTextBoxExtensions { + public static void AppendText(this RichTextBox box, string text, Color color) { + box.SelectionStart = box.TextLength; + box.SelectionLength = 0; + box.SelectionColor = color; + box.AppendText(text); + box.SelectionColor = box.ForeColor; + } + } + + /// + /// Add functions to select and deselect all items. + /// + public static class ListViewExtensions { + /// + /// Selects all items in the list view. + /// + public static void SelectAll(this ListView listView) { + // Neither I nor the Internet can figure out how to do this efficiently for + // large lists without P/Invoke interop. With 554253 lines, it takes 24.3 seconds + // to select each item individually, but only 4 milliseconds to do it through an + // LVITEM. The latter causes a single VirtualItemsSelectionRangeChanged event + // instead of 554K ItemSelectionChanged events. + // + // https://stackoverflow.com/questions/9039989/ + // https://stackoverflow.com/questions/1019388/ + +#if BUILD_FOR_WINDOWS // defined in our project properties + NativeMethods.SelectAllItems(listView); +#else + try { + Application.UseWaitCursor = true; + Cursor.Current = Cursors.WaitCursor; + listView.BeginUpdate(); + int max = listView.VirtualListSize; + for (int i = 0; i < max; i++) { + //codeListView.Items[i].Selected = true; + listView.SelectedIndices.Add(i); + } + } finally { + listView.EndUpdate(); + Application.UseWaitCursor = false; + } +#endif + } + + /// + /// Deselects all items in the list view. + /// + public static void DeselectAll(this ListView listView) { + // This is as fast as the native DeselectAllItems(), so just use it. + listView.SelectedIndices.Clear(); + } + + /// + /// Sets the double-buffered status of the list view. + /// + public static void SetDoubleBuffered(this ListView listView, bool enable) { + WinFormsUtil.SetDoubleBuffered(listView, enable); + } + + /// + /// Determines whether the specified item is visible in the list view. + /// + public static bool IsItemVisible(this ListView listView, ListViewItem item) { + Rectangle lvBounds = listView.ClientRectangle; + if (listView.HeaderStyle != ColumnHeaderStyle.None) { + // Need to factor the header height out. There's no easy way to do that, + // but the header should be (almost) the same height as an item. + // https://stackoverflow.com/q/538906/294248 + int headerHeight = item.Bounds.Height + 5; // 5 is magic, will probably break + lvBounds = new Rectangle(lvBounds.X, lvBounds.Y + headerHeight, + lvBounds.Width, lvBounds.Height - headerHeight); + } + //Console.WriteLine("IsVis LV: " + lvBounds + " IT: " + + // item.GetBounds(ItemBoundsPortion.Entire)); + return lvBounds.IntersectsWith(item.GetBounds(ItemBoundsPortion.Entire)); + } + } + + public static class WinFormsUtil { + /// + /// Sets the "DoubleBuffered" property on a Control. For some reason the + /// property is defined as "protected", but I don't want to subclass a ListView + /// just so I can enable double-buffering. + /// + /// Control to update. + /// New state. + public static void SetDoubleBuffered(Control ctrl, bool enable) { + System.Reflection.PropertyInfo prop = ctrl.GetType().GetProperty("DoubleBuffered", + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.NonPublic); + prop.SetValue(ctrl, enable, null); + } + } +} diff --git a/ImageSrc/SourceGenIcon.ico b/ImageSrc/SourceGenIcon.ico new file mode 100644 index 0000000..7e00bdf Binary files /dev/null and b/ImageSrc/SourceGenIcon.ico differ diff --git a/ImageSrc/SourceGenIcon.xcf b/ImageSrc/SourceGenIcon.xcf new file mode 100644 index 0000000..62f04d0 Binary files /dev/null and b/ImageSrc/SourceGenIcon.xcf differ diff --git a/ImageSrc/SourceGenIconSmall.ico b/ImageSrc/SourceGenIconSmall.ico new file mode 100644 index 0000000..267a343 Binary files /dev/null and b/ImageSrc/SourceGenIconSmall.ico differ diff --git a/ImageSrc/left-arrow.png b/ImageSrc/left-arrow.png new file mode 100644 index 0000000..5905050 Binary files /dev/null and b/ImageSrc/left-arrow.png differ diff --git a/ImageSrc/left-arrow.xcf b/ImageSrc/left-arrow.xcf new file mode 100644 index 0000000..36d4384 Binary files /dev/null and b/ImageSrc/left-arrow.xcf differ diff --git a/ImageSrc/right-arrow.png b/ImageSrc/right-arrow.png new file mode 100644 index 0000000..54758c7 Binary files /dev/null and b/ImageSrc/right-arrow.png differ diff --git a/MakeDist/App.config b/MakeDist/App.config new file mode 100644 index 0000000..731f6de --- /dev/null +++ b/MakeDist/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/MakeDist/CopyProgress.Designer.cs b/MakeDist/CopyProgress.Designer.cs new file mode 100644 index 0000000..596ddc5 --- /dev/null +++ b/MakeDist/CopyProgress.Designer.cs @@ -0,0 +1,103 @@ +/* + * Copyright 2018 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. + */ +namespace MakeDist { + partial class CopyProgress { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.progressRichTextBox = new System.Windows.Forms.RichTextBox(); + this.cancelButton = new System.Windows.Forms.Button(); + this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker(); + this.SuspendLayout(); + // + // progressRichTextBox + // + this.progressRichTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.progressRichTextBox.Location = new System.Drawing.Point(13, 13); + this.progressRichTextBox.Name = "progressRichTextBox"; + this.progressRichTextBox.ReadOnly = true; + this.progressRichTextBox.Size = new System.Drawing.Size(459, 507); + this.progressRichTextBox.TabIndex = 0; + this.progressRichTextBox.Text = ""; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(397, 526); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 1; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); + // + // backgroundWorker1 + // + this.backgroundWorker1.WorkerReportsProgress = true; + this.backgroundWorker1.WorkerSupportsCancellation = true; + this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork); + this.backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker1_ProgressChanged); + this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted); + // + // CopyProgress + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(484, 561); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.progressRichTextBox); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "CopyProgress"; + this.ShowInTaskbar = false; + this.Text = "CopyDistFiles"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.CopyProgress_FormClosing); + this.Load += new System.EventHandler(this.CopyProgress_Load); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.RichTextBox progressRichTextBox; + private System.Windows.Forms.Button cancelButton; + private System.ComponentModel.BackgroundWorker backgroundWorker1; + } +} \ No newline at end of file diff --git a/MakeDist/CopyProgress.cs b/MakeDist/CopyProgress.cs new file mode 100644 index 0000000..2b8842f --- /dev/null +++ b/MakeDist/CopyProgress.cs @@ -0,0 +1,134 @@ +/* + * Copyright 2018 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.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Windows.Forms; + +using CommonWinForms; + +namespace MakeDist { + public partial class CopyProgress : Form { + /// + /// Progress message, with colorful text. This is generated by the worker thread and + /// passed to the UI thread. + /// + public class ProgressMessage { + public string Text { get; private set; } + public Color Color { get; private set; } + public bool HasColor { get { return Color.A != 0; } } + + public ProgressMessage(string msg) : this(msg, Color.FromArgb(0, 0, 0, 0)) { } + + public ProgressMessage(string msg, Color color) { + Text = msg; + Color = color; + } + } + + private bool mClosedWhileRunning; + + // Copy parameters. + private FileCopier.BuildType mBuildType; + private bool mCopyTestFiles; + + + public CopyProgress(FileCopier.BuildType buildType, bool copyTestFiles) { + InitializeComponent(); + + this.Size = new Size(1200, 600); + + mBuildType = buildType; + mCopyTestFiles = copyTestFiles; + } + + private void CopyProgress_Load(object sender, EventArgs e) { + backgroundWorker1.RunWorkerAsync(); + } + + private void CopyProgress_FormClosing(object sender, FormClosingEventArgs e) { + if (backgroundWorker1.IsBusy) { + backgroundWorker1.CancelAsync(); + DialogResult = DialogResult.Cancel; + + // First close attempt is converted to a cancel. + if (!mClosedWhileRunning) { + e.Cancel = true; + mClosedWhileRunning = true; + } + } else { + DialogResult = DialogResult.OK; + } + } + + private void cancelButton_Click(object sender, EventArgs e) { + if (backgroundWorker1.IsBusy) { + backgroundWorker1.CancelAsync(); + } else { + Close(); + } + } + + private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { + BackgroundWorker worker = sender as BackgroundWorker; + + FileCopier copier = new FileCopier(mBuildType, mCopyTestFiles); + e.Result = copier.CopyAllFiles(worker); + } + + private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { + if (e.UserState is ProgressMessage) { + ProgressMessage msg = e.UserState as ProgressMessage; + if (msg.HasColor) { + progressRichTextBox.AppendText(msg.Text, msg.Color); + } else { + // plain foreground text color + progressRichTextBox.AppendText(msg.Text); + } + progressRichTextBox.SelectionStart = progressRichTextBox.Text.Length; + progressRichTextBox.ScrollToCaret(); + } else { + if (!string.IsNullOrEmpty((string)e.UserState)) { + Debug.WriteLine("Weird progress: " + e.UserState); + } + } + } + + private void backgroundWorker1_RunWorkerCompleted(object sender, + RunWorkerCompletedEventArgs e) { + if (e.Cancelled) { + Debug.WriteLine("Test halted -- user cancellation"); + } else if (e.Error != null) { + // test harness shouldn't be throwing errors like this + Debug.WriteLine("Test failed: " + e.Error.ToString()); + progressRichTextBox.AppendText("\r\n"); + progressRichTextBox.AppendText(e.Error.ToString()); + progressRichTextBox.SelectionStart = progressRichTextBox.Text.Length; + progressRichTextBox.ScrollToCaret(); + } else { + bool ok = (bool)e.Result; + Debug.WriteLine("Tests complete, success=" + ok); + } + + if (mClosedWhileRunning) { + Close(); + } + + cancelButton.Text = "Close"; + } + } +} diff --git a/MakeDist/CopyProgress.resx b/MakeDist/CopyProgress.resx new file mode 100644 index 0000000..59099f2 --- /dev/null +++ b/MakeDist/CopyProgress.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/MakeDist/FileCopier.cs b/MakeDist/FileCopier.cs new file mode 100644 index 0000000..d33e09d --- /dev/null +++ b/MakeDist/FileCopier.cs @@ -0,0 +1,316 @@ +/* + * Copyright 2018 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 System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace MakeDist { + public class FileCopier { + private const string SOURCEGEN_DIRNAME = "SourceGen"; + + /// + /// Type of build to gather files for. + /// + public enum BuildType { Unknown, Release, Debug }; + + private enum SourceFileSpec { + Unknown = 0, + All, + List, + RegressionTests, + AsmSources, + NotBins, + } + + private class CopySpec { + public string SourceDir { get; private set; } + public string DestDir { get; private set; } + public SourceFileSpec FileSpec { get; private set; } + public bool IsRecursive { get; private set; } + public string[] FileList { get; private set; } + + public CopySpec(string srcDir, string dstDir, SourceFileSpec spec, bool recursive, + string[] fileList) { + SourceDir = srcDir; + DestDir = dstDir; + FileSpec = spec; + IsRecursive = recursive; + FileList = fileList; + } + } + + private static CopySpec[] sMainSpec = { + new CopySpec(".", ".", + SourceFileSpec.List, false, new string[] { "README.md" }), + new CopySpec("Asm65/bin/{BUILD_TYPE}/netstandard2.0/", ".", + SourceFileSpec.List, false, new string[] { "Asm65.dll" }), + new CopySpec("CommonUtil/bin/{BUILD_TYPE}/netstandard2.0/", ".", + SourceFileSpec.List, false, new string[] { "CommonUtil.dll" }), + new CopySpec("CommonWinForms/bin/{BUILD_TYPE}/", ".", + SourceFileSpec.List, false, new string[] { "CommonWinForms.dll" }), + new CopySpec("PluginCommon/bin/{BUILD_TYPE}/netstandard2.0/", ".", + SourceFileSpec.List, false, new string[] { "PluginCommon.dll" }), + new CopySpec("SourceGen/bin/{BUILD_TYPE}/", ".", + SourceFileSpec.List, false, new string[] { "SourceGen.exe" }), + new CopySpec("SourceGen/RuntimeData", "RuntimeData", + SourceFileSpec.NotBins, true, null), + new CopySpec("SourceGen/Examples", "Examples", + SourceFileSpec.All, true, null), + }; + private static CopySpec[] sTestSpec = { + new CopySpec("SourceGen/SGTestData", "SGTestData", + SourceFileSpec.RegressionTests, false, null), + new CopySpec("SourceGen/SGTestData", "SGTestData", + SourceFileSpec.List, false, new string[] { "README.md" }), + new CopySpec("SourceGen/SGTestData/Expected", "SGTestData/Expected", + SourceFileSpec.AsmSources, false, null), + new CopySpec("SourceGen/SGTestData/Source", "SGTestData/Source", + SourceFileSpec.AsmSources, false, null), + new CopySpec("SourceGen/SGTestData/FunkyProjects", "SGTestData/FunkyProjects", + SourceFileSpec.All, false, null), + }; + + private static string sBasePath; + + // We want all of the regression test binaries, plus the .sym65, .dis65, and .cs, + // but nothing with an underscore in the part before the extension. + private const string TestCasePattern = @"^\d\d\d\d-[A-Za-z0-9-]+(\..*)?$"; + private static Regex sTestCaseRegex = new Regex(TestCasePattern); + + + private BuildType mBuildType; + private bool mCopyTestFiles; + private BackgroundWorker mWorker; + + + public FileCopier(BuildType buildType, bool copyTestFiles) { + mBuildType = buildType; + mCopyTestFiles = copyTestFiles; + } + + private void ReportProgress(string msg) { + mWorker.ReportProgress(0, new CopyProgress.ProgressMessage(msg + "\r\n")); + } + + private void ReportProgress(string msg, Color color) { + mWorker.ReportProgress(0, new CopyProgress.ProgressMessage(msg + "\r\n", color)); + } + + private void ReportErrMsg(string msg) { + ReportProgress(msg, Color.Red); + } + + /// + /// Main entry point. + /// + /// Background task interface object. + /// True on success. + public bool CopyAllFiles(BackgroundWorker worker) { + mWorker = worker; + + ReportProgress("Preparing... build type is " + mBuildType + ", test files are " + + (mCopyTestFiles ? "" : "NOT ") + "included."); + + string buildStr = mBuildType.ToString(); + string basePath = FindBasePath(); + Debug.Assert(basePath != null); + string distPath = Path.Combine(basePath, "DIST_" + buildStr); + + // TODO(maybe): recursively delete distPath + + if (!CopySpecList(sMainSpec, basePath, distPath, buildStr)) { + return false; + } + if (mCopyTestFiles) { + if (!CopySpecList(sTestSpec, basePath, distPath, buildStr)) { + return false; + } + } + return true; + } + + private bool CopySpecList(CopySpec[] specList, string basePath, string distPath, + string buildStr) { + foreach (CopySpec cs in specList) { + string srcDir = Path.GetFullPath(Path.Combine(basePath, + cs.SourceDir.Replace("{BUILD_TYPE}", buildStr))); + string dstDir = Path.GetFullPath(Path.Combine(distPath, cs.DestDir)); + + ReportProgress("Scanning [" + cs.FileSpec + "] " + srcDir); + + if (!CopyBySpec(srcDir, dstDir, cs.FileSpec, cs.FileList, cs.IsRecursive)) { + return false; + } + } + + return true; + } + + private bool CopyBySpec(string srcDir, string dstDir, SourceFileSpec sfspec, + string[] specFileList, bool isRecursive) { + if (!EnsureDirectoryExists(dstDir)) { + return false; + } + + string[] fileList; + if (sfspec == SourceFileSpec.List) { + fileList = specFileList; + } else { + fileList = Directory.GetFiles(srcDir); + } + + foreach (string str in fileList) { + // Spec list is filenames, GetFiles is paths; convert to simple filename. + string fileName = Path.GetFileName(str); + + switch (sfspec) { + case SourceFileSpec.All: + case SourceFileSpec.List: + // keep all + break; + case SourceFileSpec.NotBins: + // Mostly this means "skip obj and bin dirs", which happens later. + // Rather than specify everything we do want, just omit this one thing. + if (fileName == "RuntimeData.csproj") { + continue; + } + break; + case SourceFileSpec.AsmSources: + if (!fileName.ToUpperInvariant().EndsWith(".S")) { + continue; + } + break; + case SourceFileSpec.RegressionTests: + MatchCollection matches = sTestCaseRegex.Matches(fileName); + if (matches.Count != 1) { + continue; + } + // Could probably do this with regex... but why. + if (fileName.StartsWith("1") && fileName.EndsWith(".dis65")) { + continue; + } + break; + default: + throw new Exception("Unsupported spec " + sfspec); + } + + string srcPath = Path.Combine(srcDir, fileName); + string dstPath = Path.Combine(dstDir, fileName); + if (!CopyFile(srcPath, dstPath)) { + return false; + } + } + + if (isRecursive) { + string[] dirList = Directory.GetDirectories(srcDir); + + foreach (string str in dirList) { + string dirFileName = Path.GetFileName(str); + if (sfspec == SourceFileSpec.NotBins && + (dirFileName == "obj" || dirFileName == "bin")) { + continue; + } + + if (!CopyBySpec(Path.Combine(srcDir, dirFileName), + Path.Combine(dstDir, dirFileName), + sfspec, specFileList, isRecursive)) { + return false; + } + } + } + + return true; + } + + private bool EnsureDirectoryExists(string dirPath) { + if (Directory.Exists(dirPath)) { + return true; + } + if (File.Exists(dirPath)) { + ReportErrMsg("File exists and is not directory: " + dirPath); + return false; + } + try { + Directory.CreateDirectory(dirPath); + ReportProgress(" Created " + dirPath); + } catch (Exception ex) { + ReportErrMsg("Failed creating directory " + dirPath + ": " + ex.Message); + return false; + } + return true; + } + + private bool CopyFile(string srcPath, string dstPath) { + // Poll cancel button. + if (mWorker.CancellationPending) { + ReportErrMsg("Cancel\r\n"); + return false; + } + + ReportProgress(" Copy " + srcPath + " --> " + dstPath); + + try { + File.Copy(srcPath, dstPath, true); + } catch (Exception ex) { + ReportErrMsg("Failed: " + ex.Message); + return false; + } + return true; + } + + /// + /// Returns the base directory of the 6502bench installation. + /// + /// + private static string FindBasePath() { + if (sBasePath != null) { + return sBasePath; + } + + string exeName = Process.GetCurrentProcess().MainModule.FileName; + string baseDir = Path.GetDirectoryName(exeName); + if (string.IsNullOrEmpty(baseDir)) { + return null; + } + + string tryPath; + + // Use the SourceGen directory as a sentinel. + tryPath = Path.Combine(baseDir, SOURCEGEN_DIRNAME); + if (Directory.Exists(tryPath)) { + sBasePath = Path.GetFullPath(tryPath); + return sBasePath; + } + + string upThree = Path.GetDirectoryName( + Path.GetDirectoryName(Path.GetDirectoryName(baseDir))); + tryPath = Path.Combine(upThree, SOURCEGEN_DIRNAME); + if (Directory.Exists(tryPath)) { + sBasePath = Path.GetFullPath(upThree); + return sBasePath; + } + + Debug.WriteLine("Unable to find RuntimeData dir near " + exeName); + return null; + } + } +} diff --git a/MakeDist/MakeDist.Designer.cs b/MakeDist/MakeDist.Designer.cs new file mode 100644 index 0000000..9284792 --- /dev/null +++ b/MakeDist/MakeDist.Designer.cs @@ -0,0 +1,162 @@ +/* + * Copyright 2018 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. + */ +namespace MakeDist { + partial class MakeDist { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.descriptionLabel = new System.Windows.Forms.Label(); + this.distributionTypeGroupBox = new System.Windows.Forms.GroupBox(); + this.releaseDistRadio = new System.Windows.Forms.RadioButton(); + this.debugDistRadio = new System.Windows.Forms.RadioButton(); + this.includeTestsCheckBox = new System.Windows.Forms.CheckBox(); + this.goButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.distributionTypeGroupBox.SuspendLayout(); + this.SuspendLayout(); + // + // descriptionLabel + // + this.descriptionLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.descriptionLabel.Location = new System.Drawing.Point(13, 13); + this.descriptionLabel.Name = "descriptionLabel"; + this.descriptionLabel.Size = new System.Drawing.Size(371, 35); + this.descriptionLabel.TabIndex = 0; + this.descriptionLabel.Text = "This program gathers up all the files needed for a 6502bench distribution. A full" + + " debug or release build should be performed before running this."; + // + // distributionTypeGroupBox + // + this.distributionTypeGroupBox.Controls.Add(this.releaseDistRadio); + this.distributionTypeGroupBox.Controls.Add(this.debugDistRadio); + this.distributionTypeGroupBox.Location = new System.Drawing.Point(16, 51); + this.distributionTypeGroupBox.Name = "distributionTypeGroupBox"; + this.distributionTypeGroupBox.Size = new System.Drawing.Size(120, 67); + this.distributionTypeGroupBox.TabIndex = 1; + this.distributionTypeGroupBox.TabStop = false; + this.distributionTypeGroupBox.Text = "Distribution Type"; + // + // releaseDistRadio + // + this.releaseDistRadio.AutoSize = true; + this.releaseDistRadio.Location = new System.Drawing.Point(7, 20); + this.releaseDistRadio.Name = "releaseDistRadio"; + this.releaseDistRadio.Size = new System.Drawing.Size(64, 17); + this.releaseDistRadio.TabIndex = 0; + this.releaseDistRadio.TabStop = true; + this.releaseDistRadio.Text = "Release"; + this.releaseDistRadio.UseVisualStyleBackColor = true; + // + // debugDistRadio + // + this.debugDistRadio.AutoSize = true; + this.debugDistRadio.Location = new System.Drawing.Point(7, 43); + this.debugDistRadio.Name = "debugDistRadio"; + this.debugDistRadio.Size = new System.Drawing.Size(57, 17); + this.debugDistRadio.TabIndex = 1; + this.debugDistRadio.TabStop = true; + this.debugDistRadio.Text = "Debug"; + this.debugDistRadio.UseVisualStyleBackColor = true; + // + // includeTestsCheckBox + // + this.includeTestsCheckBox.AutoSize = true; + this.includeTestsCheckBox.Location = new System.Drawing.Point(16, 125); + this.includeTestsCheckBox.Name = "includeTestsCheckBox"; + this.includeTestsCheckBox.Size = new System.Drawing.Size(153, 17); + this.includeTestsCheckBox.TabIndex = 2; + this.includeTestsCheckBox.Text = "Include regression test files"; + this.includeTestsCheckBox.UseVisualStyleBackColor = true; + // + // goButton + // + this.goButton.Location = new System.Drawing.Point(192, 59); + this.goButton.Name = "goButton"; + this.goButton.Size = new System.Drawing.Size(88, 47); + this.goButton.TabIndex = 3; + this.goButton.Text = "BUILD"; + this.goButton.UseVisualStyleBackColor = true; + this.goButton.Click += new System.EventHandler(this.goButton_Click); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(309, 120); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 4; + this.cancelButton.Text = "Close"; + this.cancelButton.UseVisualStyleBackColor = true; + this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); + // + // MakeDist + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(396, 155); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.goButton); + this.Controls.Add(this.includeTestsCheckBox); + this.Controls.Add(this.distributionTypeGroupBox); + this.Controls.Add(this.descriptionLabel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "MakeDist"; + this.Text = "6502bench Distribution Maker"; + this.Load += new System.EventHandler(this.MakeDist_Load); + this.distributionTypeGroupBox.ResumeLayout(false); + this.distributionTypeGroupBox.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label descriptionLabel; + private System.Windows.Forms.GroupBox distributionTypeGroupBox; + private System.Windows.Forms.RadioButton releaseDistRadio; + private System.Windows.Forms.RadioButton debugDistRadio; + private System.Windows.Forms.CheckBox includeTestsCheckBox; + private System.Windows.Forms.Button goButton; + private System.Windows.Forms.Button cancelButton; + } +} + diff --git a/MakeDist/MakeDist.cs b/MakeDist/MakeDist.cs new file mode 100644 index 0000000..b432ada --- /dev/null +++ b/MakeDist/MakeDist.cs @@ -0,0 +1,47 @@ +/* + * Copyright 2018 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.Windows.Forms; + +namespace MakeDist { + public partial class MakeDist : Form { + public MakeDist() { + InitializeComponent(); + } + + private void MakeDist_Load(object sender, EventArgs e) { + releaseDistRadio.Checked = true; + } + + private void goButton_Click(object sender, EventArgs e) { + FileCopier.BuildType buildType; + if (releaseDistRadio.Checked) { + buildType = FileCopier.BuildType.Release; + } else { + buildType = FileCopier.BuildType.Debug; + } + bool copyTestFiles = includeTestsCheckBox.Checked; + + CopyProgress dlg = new CopyProgress(buildType, copyTestFiles); + dlg.ShowDialog(); + dlg.Dispose(); + } + + private void cancelButton_Click(object sender, EventArgs e) { + Close(); + } + } +} diff --git a/MakeDist/MakeDist.csproj b/MakeDist/MakeDist.csproj new file mode 100644 index 0000000..f603182 --- /dev/null +++ b/MakeDist/MakeDist.csproj @@ -0,0 +1,99 @@ + + + + + Debug + AnyCPU + {2415F337-2CE2-42E0-A8A7-4127FEEC94C4} + WinExe + MakeDist + MakeDist + v4.6.1 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + Form + + + CopyProgress.cs + + + + Form + + + MakeDist.cs + + + + + CopyProgress.cs + + + MakeDist.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + {08ec328d-078e-4236-b574-be6b3fd85915} + CommonWinForms + + + + \ No newline at end of file diff --git a/MakeDist/MakeDist.resx b/MakeDist/MakeDist.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/MakeDist/MakeDist.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/MakeDist/Program.cs b/MakeDist/Program.cs new file mode 100644 index 0000000..5e9668d --- /dev/null +++ b/MakeDist/Program.cs @@ -0,0 +1,31 @@ +/* + * Copyright 2018 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.Windows.Forms; + +namespace MakeDist { + static class Program { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MakeDist()); + } + } +} diff --git a/MakeDist/Properties/AssemblyInfo.cs b/MakeDist/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..52e423e --- /dev/null +++ b/MakeDist/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MakeDist")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MakeDist")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2415f337-2ce2-42e0-a8a7-4127feec94c4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MakeDist/Properties/Resources.Designer.cs b/MakeDist/Properties/Resources.Designer.cs new file mode 100644 index 0000000..5b9b6ea --- /dev/null +++ b/MakeDist/Properties/Resources.Designer.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace MakeDist.Properties { + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if ((resourceMan == null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MakeDist.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/MakeDist/Properties/Resources.resx b/MakeDist/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/MakeDist/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/MakeDist/Properties/Settings.Designer.cs b/MakeDist/Properties/Settings.Designer.cs new file mode 100644 index 0000000..ac325e7 --- /dev/null +++ b/MakeDist/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace MakeDist.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/MakeDist/Properties/Settings.settings b/MakeDist/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/MakeDist/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/PluginCommon/Interfaces.cs b/PluginCommon/Interfaces.cs new file mode 100644 index 0000000..bebdfc2 --- /dev/null +++ b/PluginCommon/Interfaces.cs @@ -0,0 +1,134 @@ +/* + * Copyright 2018 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; + +namespace PluginCommon { + /// + /// Script "plugins" must implement this interface. + /// + public interface IPlugin { + /// + /// Identification string. Contents are arbitrary, but should briefly identify the + /// purpose of the plugin, e.g. "Apple II ProDOS 8 MLI call handler". It may + /// contain version information, but should not be expected to be machine-readable. + /// + string Identifier { get; } + + /// + /// Returns true if this plugin checks JSR/JSL for inline data. + /// + //bool HasInlineDataAnalyzer { get; } + + /// + /// Initializes the plugin with an application reference and a buffer with file + /// data. Called before each analysis pass. + /// + /// In the current implementation, the file data will be the same every time, + /// because plugins are discarded when a project is closed. However, this may + /// change if we add a descramble feature. + /// + /// Reference to application interface. + /// 65xx code and data. + /// Platform symbols, in no particular order. + void Prepare(IApplication appRef, byte[] fileData, List platSyms); + + /// + /// Checks to see if code/data near a JSR instruction should be formatted. + /// + /// The file data is guaranteed to hold the JSR (offset + 2). + /// + /// Offset of the JSR instruction. + /// Set to true if the JSR doesn't actually return. + void CheckJsr(int offset, out bool noContinue); + + /// + /// Checks to see if code/data near a JSL instruction should be formatted. + /// + /// The file data is guaranteed to hold the JSL (offset + 3). + /// + /// Offset of the JSL instruction. + /// Set to true if the JSL doesn't actually return. + void CheckJsl(int offset, out bool noContinue); + } + + /// + /// Interfaces provided by the application for use by plugins. + /// + public interface IApplication { + /// + /// Sends a debug message to the application. This can be useful when debugging scripts. + /// (Use DEBUG > Show Analyzer Output to view it.) + /// + /// Message to send. + void DebugLog(string msg); + + /// + /// Specifies operand formatting. + /// + /// File offset of opcode. + /// Sub-type. Must be appropriate for NumericLE. + /// Optional symbolic label. + /// True if the change was made, false if it was rejected. + bool SetOperandFormat(int offset, DataSubType subType, string label); + + /// + /// Formats file data as inline data. + /// + /// File offset. + /// Length of item. + /// Type of item. Must be NumericLE, NumericBE, or Dense. + /// Sub-type. Must be appropriate for type. + /// Optional symbolic label. + /// True if the change was made, false if it was rejected. + bool SetInlineDataFormat(int offset, int length, DataType type, + DataSubType subType, string label); + + // Might want to add: + // int AddressToOffset(int address) // returns 24-bit offset, or -1 if outside file + // int OffsetToAddress(int offset) // returns 24-bit address + // (although we could also just pass the address map in at Prepare() -- more efficient + // if this gets called frequently) + } + + /// + /// Data format type. + /// + public enum DataType { + Unknown = 0, + NumericLE, + NumericBE, + String, + Dense, + Fill + } + + /// + /// Data format sub-type. + /// + public enum DataSubType { + // No sub-type specified. + None = 0, + + // For NumericLE/BE + Hex, + Decimal, + Binary, + Ascii, + Address, + Symbol + } +} diff --git a/PluginCommon/PlatSym.cs b/PluginCommon/PlatSym.cs new file mode 100644 index 0000000..9862100 --- /dev/null +++ b/PluginCommon/PlatSym.cs @@ -0,0 +1,84 @@ +/* + * Copyright 2018 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.Text; + +namespace PluginCommon { + /// + /// Symbols loaded from platform symbol files, for use in extension scripts. + /// + /// Instances are immutable. + /// + [Serializable] + public class PlatSym { + public string Label { get; private set; } + public int Value { get; private set; } + public string Tag { get; private set; } + + /// + /// Nullary constructor, for deserialization. + /// + private PlatSym() { } + + /// + /// Constructor. + /// + /// Symbol label. + /// Symbol value. + /// Symbol group tag. + public PlatSym(string label, int value, string tag) { + Label = label; + Value = value; + Tag = tag; + } + + /// + /// Generates a dictionary of platform symbols, keyed by value. Only symbols with + /// a matching tag are included. If more than one symbol has the same value, only + /// one will be included; which one it will be is undefined. + /// + /// List of platform symbols to select from. + /// Tag to match, or null to collect all symbols. + /// Application reference, for debug log output. + /// + public static Dictionary GenerateValueList(List platSyms, + string tag, IApplication appRef) { + Dictionary dict = new Dictionary(); + + foreach (PlatSym ps in platSyms) { + if (tag == null || tag == ps.Tag) { + try { + dict.Add(ps.Value, ps); + } catch (ArgumentException) { + appRef.DebugLog("WARNING: GenerateValueList: multiple entries with " + + "value " + ps.Value.ToString("x4")); + } + } + } + + if (dict.Count == 0) { + appRef.DebugLog("PlatSym: no symbols found for tag=" + tag); + } + + return dict; + } + + public override string ToString() { + return Label + "=" + Value.ToString("x4") + " [" + Tag + "]"; + } + } +} diff --git a/PluginCommon/PluginCommon.csproj b/PluginCommon/PluginCommon.csproj new file mode 100644 index 0000000..d0b956e --- /dev/null +++ b/PluginCommon/PluginCommon.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/PluginCommon/PluginManager.cs b/PluginCommon/PluginManager.cs new file mode 100644 index 0000000..bc13afb --- /dev/null +++ b/PluginCommon/PluginManager.cs @@ -0,0 +1,205 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Threading; + +namespace PluginCommon { + /// + /// Manages loaded plugins, in the "remote" AppDomain. + /// + public sealed class PluginManager : MarshalByRefObject { + /// + /// Collection of instances of active plugins, keyed by script identifier. Other + /// plugin assemblies may be present in the AppDomain, but have not been identified + /// by the application as being of interest. + /// + private Dictionary mActivePlugins = new Dictionary(); + + /// + /// Reference to file data. + /// + private byte[] mFileData; + + + /// + /// Constructor, invoked from CreateInstanceAndUnwrap(). + /// + public PluginManager() { + Debug.WriteLine("PluginManager ctor (id=" + AppDomain.CurrentDomain.Id + ")"); + + // Seems to require [SecurityCritical] + //Type lsc = Type.GetType("System.Runtime.Remoting.Lifetime.LifetimeServices"); + //PropertyInfo prop = lsc.GetProperty("LeaseTime"); + //prop.SetValue(null, TimeSpan.FromSeconds(30)); + } + + ~PluginManager() { + Debug.WriteLine("~PluginManager (id=" + AppDomain.CurrentDomain.Id + ")"); + } + + /// + /// Sets the file data to use for all plugins. + /// + /// The file data argument will be an AppDomain-local copy of the data, made by the + /// argument marshalling code. So plugins can scribble all over it without trashing + /// the original. We want to store it in PluginManager so we don't make a new copy + /// for each individual plugin. + /// + /// 65xx code and data. + public void SetFileData(byte[] fileData) { + mFileData = fileData; + } + + /// + /// Tests simple round-trip communication. This may be called from an arbitrary thread. + /// + public int Ping(int val) { + Debug.WriteLine("PluginManager Ping tid=" + Thread.CurrentThread.ManagedThreadId + + " (id=" + AppDomain.CurrentDomain.Id + "): " + val); + return val + 1; + } + + /// + /// Creates a plugin instance from a compiled assembly. Pass in the script identifier + /// for future lookups. If the plugin has already been instantiated, that object + /// will be returned. + /// + /// Full path to compiled assembly. + /// Identifier to use in e.g. GetPlugin(). + /// Reference to plugin instance. + public IPlugin LoadPlugin(string dllPath, string scriptIdent) { + if (mActivePlugins.TryGetValue(dllPath, out IPlugin ip)) { + Debug.WriteLine("PM: returning cached plugin for " + dllPath); + return ip; + } + + Assembly asm = Assembly.LoadFile(dllPath); + + foreach (Type type in asm.GetExportedTypes()) { + // Using a System.Linq extension method. + if (type.IsClass && !type.IsAbstract && + type.GetInterfaces().Contains(typeof(IPlugin))) { + + ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes); + IPlugin iplugin = (IPlugin)ctor.Invoke(null); + Debug.WriteLine("PM created instance: " + iplugin); + mActivePlugins.Add(scriptIdent, iplugin); + return iplugin; + } + } + throw new Exception("No IPlugin class found in " + dllPath); + } + + /// + /// Gets an instance of a previously-loaded plugin. + /// + /// Script identifier that was passed to LoadPlugin(). + /// Reference to instance of plugin. + public IPlugin GetPlugin(string scriptIdent) { + if (mActivePlugins.TryGetValue(scriptIdent, out IPlugin plugin)) { + return plugin; + } + return null; + } + + /// + /// Returns a string with the assembly's location. + /// + public string GetPluginAssemblyLocation(IPlugin plugin) { + return plugin.GetType().Assembly.Location; + } + + /// + /// Generates a list of references to instances of active plugins. + /// + /// Newly-created list of plugin references. + public List GetActivePlugins() { + List list = new List(mActivePlugins.Count); + foreach (KeyValuePair kvp in mActivePlugins) { + list.Add(kvp.Value); + } + Debug.WriteLine("PluginManager: returning " + list.Count + " plugins (id=" + + AppDomain.CurrentDomain.Id + ")"); + return list; + } + + /// + /// Clears the list of loaded plugins. This does not unload the assemblies from + /// the AppDomain. + /// + public void ClearPluginList() { + mActivePlugins.Clear(); + } + + /// + /// Invokes the Prepare() method on all active plugins. + /// + /// Reference to host object providing app services. + public void PreparePlugins(IApplication appRef, List platSyms) { + foreach (KeyValuePair kvp in mActivePlugins) { + kvp.Value.Prepare(appRef, mFileData, platSyms); + } + } + +#if false + /// + /// DEBUG ONLY: establish a fast lease timeout. Normally the lease + /// is five minutes; this reduces it to a few seconds. (The actual time is + /// also affected by LifetimeServices.LeaseManagerPollTime, which defaults + /// to 10 seconds.) + /// + /// Unfortunately this must be tagged [SecurityCritical] to match the method being + /// overridden, but in a partially-trusted sandbox that's not allowed. You have + /// to relax security entirely for this to work. + /// + //[SecurityPermissionAttribute(SecurityAction.Demand, + // Flags = SecurityPermissionFlag.Infrastructure)] + [System.Security.SecurityCritical] + public override object InitializeLifetimeService() { + object lease = base.InitializeLifetimeService(); + + // netstandard2.0 doesn't have System.Runtime.Remoting.Lifetime, so use reflection + PropertyInfo leaseState = lease.GetType().GetProperty("CurrentState"); + PropertyInfo initialLeaseTime = lease.GetType().GetProperty("InitialLeaseTime"); + PropertyInfo sponsorshipTimeout = lease.GetType().GetProperty("SponsorshipTimeout"); + PropertyInfo renewOnCallTime = lease.GetType().GetProperty("RenewOnCallTime"); + + Console.WriteLine("Default lease: ini=" + + initialLeaseTime.GetValue(lease) + " spon=" + + sponsorshipTimeout.GetValue(lease) + " renOC=" + + renewOnCallTime.GetValue(lease)); + + if ((int)leaseState.GetValue(lease) == 1 /*LeaseState.Initial*/) { + // Initial lease duration. + initialLeaseTime.SetValue(lease, TimeSpan.FromSeconds(8)); + + // How long we will wait for the sponsor to respond + // with a lease renewal time. + sponsorshipTimeout.SetValue(lease, TimeSpan.FromSeconds(5)); + + // Each call to the remote object extends the lease so that + // it has at least this much time left. + renewOnCallTime.SetValue(lease, TimeSpan.FromSeconds(2)); + } + return lease; + } +#endif + } +} diff --git a/PluginCommon/Util.cs b/PluginCommon/Util.cs new file mode 100644 index 0000000..d1fefca --- /dev/null +++ b/PluginCommon/Util.cs @@ -0,0 +1,50 @@ +/* + * Copyright 2018 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 CommonUtil; + +namespace PluginCommon { + /// + /// Utility functions available for plugins to use. + /// + /// The idea is to make CommonUtil functions available to plugins while isolating + /// them from changes to the library. Anything here is guaranteed to keep working, + /// while other classes and functions in CommonUtil may change between releases. + /// + public static class Util { + /// + /// Extracts an integer from the data stream. + /// + /// Raw data stream. + /// Start offset. + /// Word width, which may be 1-4 bytes. + /// True if word is in big-endian order. + /// Value found. + public static int GetWord(byte[] data, int offset, int width, bool isBigEndian) { + return RawData.GetWord(data, offset, width, isBigEndian); + } + + /// + /// Compute a standard CRC-32 (polynomial 0xedb88320) on a buffer of data. + /// + /// Buffer to process. + /// CRC value. + public static uint ComputeBufferCRC(byte[] data) { + return CRC32.OnBuffer(0, data, 0, data.Length); + } + } +} diff --git a/README.md b/README.md index 6b2d317..5f27b05 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,105 @@ -# 6502bench -A workbench for developing 6502 code +# 6502bench # + +6502bench is a code development "workbench" for 6502, 65C02, and 65802/65816 +code. It currently features one tool, the SourceGen disassembler. + +You can download the source code and build it yourself, or click the +"Releases" tab near the top of the github page for pre-built downloads. + + +## SourceGen ## + +SourceGen converts machine-language programs to assembly-language source +code. It has most of the features you will find in other 6502 disassemblers, +as well as many less-common ones. + +A demo video is available: https://youtu.be/dalISyBPQq8 + +#### Features #### + +Analysis: +- Support for 6502 (including undocumented opcodes), 65C02, and 65816. +- Code flow analyzer traces execution to find all reachable + instructions. Hinting mechanism allows manual specification of + entry points. +- Processor status flags are tracked, allowing automatic detection + of branch-always and branch-never, as well as register widths on + 16-bit CPUs. The analyzer tracks these across subroutine calls and + branches. Cycle counts factor these in automatically. +- Editable labels are generated for every branch and data target. +- Automatic detection and classification of ASCII strings and runs of + identical bytes. +- All target-platform-specific stuff is stored in plain text files + loaded at runtime. This includes symbols for ROM entry points + and standard zero-page locations. Additional symbols and overrides + can be specified at the project level. +- Extension scripts can be used to reformat code and identify inline + data that follows JSR/JSL. Code is compiled at run time and + executes in a sandbox. + +UI: +- Fully interactive point-and-click GUI. Add labels and multi-line + comments, change addresses, and see the changes immediately. +- Instruction operand formats (hex, decimal, etc) can be set for + individual lines. Symbols that don't match the operands are automatically + offset, allowing simple expressions like "address - 1". +- Full-line comments are automatically word-wrapped, and can be + "boxed" for an authentic retro feel. +- Data areas can be formatted as bytes, words, addresses, and more. + Several types of strings are recognized (null-terminated, length + prefixed, etc). +- "Infinite" undo/redo of all actions. +- Notes can be added that aren't included in generated output. Very + useful for marking up a work in progress. +- Cross-reference tables are generated for every branch and data + target address, as well as for external platform symbols. +- Instruction summaries, including cpu cycles and flags modified, are + shown along with a description of the opcode function. +- Display is configurable for upper/lower case, choice of pseudo-op + names, expression formats, and more. + +Output: +- Assembly source can be generated for multiple assemblers (currently + cc65 and Merlin 32). +- Cross-assemblers can be launched directly from the generation GUI to + verify output correctness. +- Optional automatic conversion of labels from global to local. +- Symbols may be exported from one project and imported into another + to facilitate multi-binary disassembly. + +Misc: +- Preset project attributes (CPU type, platform symbol file sets) are defined + for a variety of platforms. +- Project file is stored in a text format, and only holds metadata. None + of the original file is included, allowing the project file to be shared + without violating copyrights (note: may vary depending on local laws). + +There are a couple of significant areas where support is currently lacking: +- Poor support for multi-bank 65816 files (IIgs OMF, SNES). +- No support for alternate character sets (e.g. PETSCII). + + +## About the Code ## + +All of the code is written in C# .NET, using the (free to download) Visual +Studio Community 2017 IDE as the primary development environment. The user +interface uses the WinForms API. Efforts have been made to avoid doing +anything Windows-specific, in the hope that the applications will be +straightforward to port to other platforms. + +The solution is called "WorkBench.sln" rather than "6502bench.sln" because +some things in Visual Studio got weird when it didn't start with a letter. + +The code style is closer to what Android uses than "standard" C#. Lines +are folded to fit 100 columns. + +The source code is licensed under Apache 2.0 +(http://www.apache.org/licenses/LICENSE-2.0), which makes it free for use in +both open-source programs and closed-source commercial software. The license +terms are similar to BSD or MIT, but with some additional constraints on +patent licensing. (This is the same license Google uses for the Android +Open Source Project.) + +Images are licensed under Creative Commons ShareAlike 4.0 International +(https://creativecommons.org/licenses/by-sa/4.0/). + diff --git a/SourceGen/AddressMap.cs b/SourceGen/AddressMap.cs new file mode 100644 index 0000000..6061a46 --- /dev/null +++ b/SourceGen/AddressMap.cs @@ -0,0 +1,284 @@ +/* + * Copyright 2018 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; +using System.Collections.Generic; +using System.Diagnostics; + +namespace SourceGen { + /// + /// Map file offsets to 65xx addresses and vice-versa. Useful for sources with + /// multiple ORG directives. + /// + /// It's possible to generate code that would overlap once relocated at run time, + /// which means a given address could map to multiple offsets. For this reason + /// it's useful to know the offset of the referring code when evaluating a + /// reference, so that a "local" match can take priority. + /// + public class AddressMap : IEnumerable { + /// + /// Code starting at the specified offset will have the specified address. + /// + /// The entries are held in the list in order, sorted by offset, with no gaps. + /// This makes the "length" field redundant, as it can be computed by + /// (entry[N+1].mOffset - entry[N].mOffset), with a special case for the last + /// entry in the list. It's convenient to maintain it explicitly however, as + /// the list is read far more often than it is updated. + /// + /// Entries are mutable, but must only be altered by AddressMap. Don't retain + /// instances of this across other activity. + /// + public class AddressMapEntry { + public int Offset { get; set; } + public int Addr { get; set; } + public int Length { get; set; } + + public AddressMapEntry(int offset, int addr, int len) { + Offset = offset; + Addr = addr; + Length = len; + } + } + + /// + /// Total length, in bytes, spanned by this map. + /// + private int mTotalLength; + + /// + /// List of definitions, in sorted order. + /// + private List mAddrList = new List(); + + /// + /// Constructor. + /// + /// Total length, in bytes, spanned by this map. + public AddressMap(int length) { + /// There must always be at least one entry, defining the target address + /// for file offset 0. This can be changed, but can't be removed. + mTotalLength = length; + mAddrList.Add(new AddressMapEntry(0, 0, length)); + } + + // IEnumerable + public IEnumerator GetEnumerator() { + return ((IEnumerable)mAddrList).GetEnumerator(); + } + + // IEnumerable + IEnumerator IEnumerable.GetEnumerator() { + return ((IEnumerable)mAddrList).GetEnumerator(); + } + + /// + /// Returns the address map entry index associated with the specified offset, or -1 + /// if there is no address map entry there. + /// + public int Get(int offset) { + foreach (AddressMapEntry ad in mAddrList) { + if (ad.Offset == offset) { + return ad.Addr; + } + } + return -1; + } + + /// + /// Adds, updates, or removes a map entry. + /// + /// File offset at which the address changes. + /// 24-bit address. + public void Set(int offset, int addr) { + Debug.Assert(offset >= 0); + if (addr == -1) { + if (offset != 0) { // ignore attempts to remove entry at offset zero + Remove(offset); + } + return; + } + Debug.Assert(addr >= 0 && addr < 0x01000000); // 24-bit address space + + int i; + for (i = 0; i < mAddrList.Count; i++) { + AddressMapEntry ad = mAddrList[i]; + if (ad.Offset == offset) { + // update existing + ad.Addr = addr; + mAddrList[i] = ad; + return; + } else if (ad.Offset > offset) { + // The i'th entry is one past the interesting part. + break; + } + } + + // Carve a chunk out of the previous entry. + AddressMapEntry prev = mAddrList[i - 1]; + int prevOldLen = prev.Length; + int prevNewLen = offset - prev.Offset; + prev.Length = prevNewLen; + mAddrList[i - 1] = prev; + + mAddrList.Insert(i, + new AddressMapEntry(offset, addr, prevOldLen - prevNewLen)); + + DebugValidate(); + } + + /// + /// Removes an entry from the set. + /// + /// The initial offset of the mapping to remove. This + /// must be the initial value, not a mid-range value. + /// True if something was removed. + public bool Remove(int offset) { + if (offset == 0) { + throw new Exception("Not allowed to remove entry 0"); + } + + for (int i = 1; i < mAddrList.Count; i++) { + if (mAddrList[i].Offset == offset) { + // Add the length to the previous entry. + AddressMapEntry prev = mAddrList[i - 1]; + prev.Length += mAddrList[i].Length; + mAddrList[i - 1] = prev; + + mAddrList.RemoveAt(i); + DebugValidate(); + return true; + } + } + return false; + } + + /// + /// Returns the index of the address map entry that contains the given offset. + /// We assume the offset is valid. + /// + private int IndexForOffset(int offset) { + for (int i = 1; i < mAddrList.Count; i++) { + if (mAddrList[i].Offset > offset) { + return i - 1; + } + } + + return mAddrList.Count - 1; + } + + /// + /// Returns true if the given address falls into the range spanned by the + /// address map entry. + /// + /// Address map entry index. + /// Address to check. + /// + private bool IndexContainsAddress(int index, int addr) { + return addr >= mAddrList[index].Addr && + addr < mAddrList[index].Addr + mAddrList[index].Length; + } + + /// + /// Determines the file offset that best contains the specified target address. + /// + /// Offset of the address reference. + /// Address to look up. + /// The file offset, or -1 if the address falls outside the file. + public int AddressToOffset(int srcOffset, int targetAddr) { + if (mAddrList.Count == 1) { + // Trivial case. + if (IndexContainsAddress(0, targetAddr)) { + Debug.Assert(targetAddr >= mAddrList[0].Addr); + return targetAddr - mAddrList[0].Addr; + } else { + return -1; + } + } + + // We have multiple, potentially overlapping address ranges. Start by + // looking for a match in the srcOffset range; if that fails, scan + // forward from the start. + int srcOffIndex = IndexForOffset(srcOffset); + if (IndexContainsAddress(srcOffIndex, targetAddr)) { + Debug.Assert(targetAddr >= mAddrList[srcOffIndex].Addr); + return (targetAddr - mAddrList[srcOffIndex].Addr) + mAddrList[srcOffIndex].Offset; + } + + for (int i = 0; i < mAddrList.Count; i++) { + if (i == srcOffIndex) { + // optimization -- we already checked this one + continue; + } + if (IndexContainsAddress(i, targetAddr)) { + Debug.Assert(targetAddr >= mAddrList[i].Addr); + return (targetAddr - mAddrList[i].Addr) + mAddrList[i].Offset; + } + } + + return -1; + } + + /// + /// Converts a file offset to an address. + /// + /// File offset. + /// 24-bit address. + public int OffsetToAddress(int offset) { + int srcOffIndex = IndexForOffset(offset); + return mAddrList[srcOffIndex].Addr + (offset - mAddrList[srcOffIndex].Offset); + } + + + /// + /// Internal consistency checks. + /// + private void DebugValidate() { + if (mAddrList.Count < 1) { + throw new Exception("AddressMap: empty"); + } + if (mAddrList[0].Offset != 0) { + throw new Exception("AddressMap: bad offset 0"); + } + + if (mAddrList.Count == 1) { + if (mAddrList[0].Length != mTotalLength) { + throw new Exception("AddressMap: single entry len bad"); + } + } else { + int totalLen = 0; + for (int i = 0; i < mAddrList.Count; i++) { + AddressMapEntry ent = mAddrList[i]; + if (i != 0) { + if (ent.Offset != mAddrList[i - 1].Offset + mAddrList[i - 1].Length) { + throw new Exception("Bad offset step to " + i); + } + } + + totalLen += ent.Length; + } + + if (totalLen != mTotalLength) { + throw new Exception("AddressMap: bad length sum (" + totalLen + " vs " + + mTotalLength + ")"); + } + } + } + + public override string ToString() { + return "[AddressMap: " + mAddrList.Count + " entries]"; + } + } +} diff --git a/SourceGen/Anattrib.cs b/SourceGen/Anattrib.cs new file mode 100644 index 0000000..3a38bc0 --- /dev/null +++ b/SourceGen/Anattrib.cs @@ -0,0 +1,355 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Text; + +using Asm65; + +namespace SourceGen { + /// + /// Analyzer attribute holder. Contains the output of the instruction and data analyzers. + /// Every byte in the input file has one of these associated with it. + /// + /// (Yes, it's a mutable struct. Yes, that fact has bitten me a few times. The array + /// of these may have millions of elements, so the reduction in overhead seems worthwhile.) + /// + public struct Anattrib { + [FlagsAttribute] + private enum AttribFlags { + InstrStart = 1 << 0, // byte is first of an instruction + Instruction = 1 << 1, // byte is part of an instruction or inline data + InlineData = 1 << 2, // byte is inline data + Data = 1 << 3, // byte is data + + EntryPoint = 1 << 8, // external code branches here + BranchTarget = 1 << 9, // internal code branches here + ExternalBranch = 1 << 10, // this abs/rel branch lands outside input file + + NoContinue = 1 << 12, // execution does not continue to following instruction + + Visited = 1 << 16, // has the analyzer visited this byte? + Changed = 1 << 17, // set/cleared as the analyzer works + + Hinted = 1 << 18, // was this byte affected by a type hint? + } + + // Flags indicating what type of data is here. Use the following Is* properties + // to set/clear. + private AttribFlags mAttribFlags; + + public bool IsInstructionStart { + get { + return (mAttribFlags & AttribFlags.InstrStart) != 0; + } + set { + IsInstruction = value; + if (value) { + mAttribFlags |= AttribFlags.InstrStart; + } else { + mAttribFlags &= ~AttribFlags.InstrStart; + } + } + } + public bool IsInstruction { + get { + return (mAttribFlags & AttribFlags.Instruction) != 0; + } + set { + Debug.Assert(value == false || + (mAttribFlags & (AttribFlags.InlineData | AttribFlags.Data)) == 0); + if (value) { + mAttribFlags |= AttribFlags.Instruction; + } else { + mAttribFlags &= ~AttribFlags.Instruction; + } + } + } + public bool IsInlineData { + get { + return (mAttribFlags & AttribFlags.InlineData) != 0; + } + set { + Debug.Assert(value == false || + (mAttribFlags & (AttribFlags.Instruction | AttribFlags.Data)) == 0); + if (value) { + mAttribFlags |= AttribFlags.InlineData; + } else { + mAttribFlags &= ~AttribFlags.InlineData; + } + } + } + public bool IsData { + get { + return (mAttribFlags & AttribFlags.Data) != 0; + } + set { + Debug.Assert(value == false || + (mAttribFlags & (AttribFlags.InlineData | AttribFlags.Instruction)) == 0); + if (value) { + mAttribFlags |= AttribFlags.Data; + } else { + mAttribFlags &= ~AttribFlags.Data; + } + } + } + public bool IsStart { + get { + return IsInstructionStart || IsDataStart || IsInlineDataStart; + } + } + public bool IsEntryPoint { + get { + return (mAttribFlags & AttribFlags.EntryPoint) != 0; + } + set { + if (value) { + mAttribFlags |= AttribFlags.EntryPoint; + } else { + mAttribFlags &= ~AttribFlags.EntryPoint; + } + } + } + public bool IsBranchTarget { + get { + return (mAttribFlags & AttribFlags.BranchTarget) != 0; + } + set { + if (value) { + mAttribFlags |= AttribFlags.BranchTarget; + } else { + mAttribFlags &= ~AttribFlags.BranchTarget; + } + } + } + public bool IsExternalBranch { + get { + return (mAttribFlags & AttribFlags.ExternalBranch) != 0; + } + set { + if (value) { + mAttribFlags |= AttribFlags.ExternalBranch; + } else { + mAttribFlags &= ~AttribFlags.ExternalBranch; + } + } + } + public bool DoesNotContinue { + get { + return (mAttribFlags & AttribFlags.NoContinue) != 0; + } + set { + if (value) { + mAttribFlags |= AttribFlags.NoContinue; + } else { + mAttribFlags &= ~AttribFlags.NoContinue; + } + } + } + public bool DoesNotBranch { + get { + return (BranchTaken == OpDef.BranchTaken.Never); + } + } + public bool IsVisited { + get { + return (mAttribFlags & AttribFlags.Visited) != 0; + } + set { + if (value) { + mAttribFlags |= AttribFlags.Visited; + } else { + mAttribFlags &= ~AttribFlags.Visited; + } + } + } + public bool IsChanged { + get { + return (mAttribFlags & AttribFlags.Changed) != 0; + } + set { + if (value) { + mAttribFlags |= AttribFlags.Changed; + } else { + mAttribFlags &= ~AttribFlags.Changed; + } + } + } + public bool IsHinted { + get { + return (mAttribFlags & AttribFlags.Hinted) != 0; + } + set { + if (value) { + mAttribFlags |= AttribFlags.Hinted; + } else { + mAttribFlags &= ~AttribFlags.Hinted; + } + } + } + + public bool IsDataStart { + get { + return IsData && DataDescriptor != null; + } + } + public bool IsInlineDataStart { + get { + return IsInlineData && DataDescriptor != null; + } + } + + /// + /// Get the target memory address for this byte. + /// + public int Address { get; set; } + + /// + /// Instructions: length of the instruction (for InstrStart). If a FormatDescriptor + /// is assigned, the length must match. + /// Inline data: FormatDescriptor length, or zero if no descriptor is defined. + /// Data: FormatDescriptor length, or zero if no descriptor is defined. + /// + /// This field should only be set by CodeAnalysis methods, although the "get" value + /// can be changed for data/inline-data by setting the DataDescriptor field. + /// + public int Length { + get { + // For data we don't even use the field; this ensures that we're always + // using the FormatDescriptor's length. + if (IsData || IsInlineData) { + Debug.Assert(mLength == 0); + if (DataDescriptor != null) { + return DataDescriptor.Length; + } else { + return 0; + } + } + return mLength; + } + set { + Debug.Assert(!IsData); + mLength = value; + } + } + private int mLength; + + /// + /// Instructions only: processor status flags. + /// + /// Note this returns a copy of a struct, so modifications to the returned value + /// (including calls to Merge and Apply) are not permanent. + /// + public StatusFlags StatusFlags { + get { return mStatusFlags; } + set { mStatusFlags = value; } + } + private StatusFlags mStatusFlags; + + public void MergeStatusFlags(StatusFlags other) { + mStatusFlags.Merge(other); + } + public void ApplyStatusFlags(StatusFlags other) { + mStatusFlags.Apply(other); + } + + /// + /// Branch instructions only: outcome of branch. + /// + public OpDef.BranchTaken BranchTaken { get; set; } + + /// + /// Instructions only: decoded operand address value. Will be -1 if not + /// yet computed or not applicable. For a relative branch instruction, + /// this will have the absolute branch target address. On the 65816, this + /// will be a 24-bit address. + /// + public int OperandAddress { + get { return mOperandAddressSet ? mOperandAddress : -1; } + set { + Debug.Assert(mOperandAddress >= -1); + mOperandAddress = value; + mOperandAddressSet = (value >= 0); + } + } + private int mOperandAddress; + private bool mOperandAddressSet; + + /// + /// Instructions only: offset referenced by OperandAddress. Will be -1 if not + /// yet computed, not applicable, or if OperandAddress refers to a location + /// outside the scope of the file. + /// + public int OperandOffset { + get { return mOperandOffsetSet ? mOperandOffset : -1; } + set { + Debug.Assert(mOperandOffset >= -1); + mOperandOffset = value; + mOperandOffsetSet = (value >= 0); + } + } + private int mOperandOffset; + private bool mOperandOffsetSet; + + /// + /// Instructions only: is OperandOffset a direct target offset? (This is used when + /// tracing jump instructions, to know if we should add the offset to the scan list. + /// It's determined by the opcode, e.g. "JMP addr" -> true, "JMP (addr,X)" -> false.) + /// + public bool IsOperandOffsetDirect { get; set; } + + /// + /// Symbol defined as the label for this offset. All offsets that are instruction + /// or data target offsets will have one of these defined. Users can define additional + /// symbols as well. + /// + /// Will be null if no label is defined for this offset. + /// + public Symbol Symbol { get; set; } + + /// + /// Format descriptor for operands and data items. Will be null if no descriptor + /// is defined for this offset. + /// + public FormatDescriptor DataDescriptor { get; set; } + + /// + /// Is this an instruction with an operand (i.e. not impl/acc)? + /// + public bool IsInstructionWithOperand { + get { + if (!IsInstructionStart) { + return false; + } + return Length != 1; + } + } + + /// + /// Returns a fixed-width string with indicators for items of interest. + /// + public string ToAttrString() { + StringBuilder sb = new StringBuilder(5); + char blank = '.'; + sb.Append(IsEntryPoint ? '@' : blank); + sb.Append(IsHinted ? 'H' : blank); + sb.Append(DoesNotBranch ? '!' : blank); + sb.Append(DoesNotContinue ? '#' : blank); + sb.Append(IsBranchTarget ? '>' : blank); + return sb.ToString(); + } + } +} diff --git a/SourceGen/App.config b/SourceGen/App.config new file mode 100644 index 0000000..731f6de --- /dev/null +++ b/SourceGen/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/SourceGen/AppForms/AboutBox.Designer.cs b/SourceGen/AppForms/AboutBox.Designer.cs new file mode 100644 index 0000000..ff759ff --- /dev/null +++ b/SourceGen/AppForms/AboutBox.Designer.cs @@ -0,0 +1,187 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class AboutBox { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.boardPictureBox = new System.Windows.Forms.PictureBox(); + this.sourceGenLabel = new System.Windows.Forms.Label(); + this.versionLabel = new System.Windows.Forms.Label(); + this.createdLabel = new System.Windows.Forms.Label(); + this.okButton = new System.Windows.Forms.Button(); + this.legalStuffTextBox = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.osPlatformLabel = new System.Windows.Forms.Label(); + this.debugEnabledLabel = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)(this.boardPictureBox)).BeginInit(); + this.SuspendLayout(); + // + // boardPictureBox + // + this.boardPictureBox.Location = new System.Drawing.Point(13, 13); + this.boardPictureBox.Name = "boardPictureBox"; + this.boardPictureBox.Size = new System.Drawing.Size(320, 236); + this.boardPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.boardPictureBox.TabIndex = 0; + this.boardPictureBox.TabStop = false; + // + // sourceGenLabel + // + this.sourceGenLabel.AutoSize = true; + this.sourceGenLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 24F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.sourceGenLabel.Location = new System.Drawing.Point(340, 13); + this.sourceGenLabel.Name = "sourceGenLabel"; + this.sourceGenLabel.Size = new System.Drawing.Size(349, 37); + this.sourceGenLabel.TabIndex = 1; + this.sourceGenLabel.Text = "6502bench SourceGen"; + // + // versionLabel + // + this.versionLabel.AutoSize = true; + this.versionLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 20.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.versionLabel.Location = new System.Drawing.Point(340, 60); + this.versionLabel.Name = "versionLabel"; + this.versionLabel.Size = new System.Drawing.Size(273, 31); + this.versionLabel.TabIndex = 2; + this.versionLabel.Text = "Version X.Y.Z Alpha1"; + // + // createdLabel + // + this.createdLabel.AutoSize = true; + this.createdLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.createdLabel.Location = new System.Drawing.Point(407, 142); + this.createdLabel.Name = "createdLabel"; + this.createdLabel.Size = new System.Drawing.Size(206, 40); + this.createdLabel.TabIndex = 3; + this.createdLabel.Text = "Copyright 2018 faddenSoft\r\nCreated by Andy McFadden\r\n"; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(617, 526); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 0; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + // + // legalStuffTextBox + // + this.legalStuffTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.legalStuffTextBox.Location = new System.Drawing.Point(12, 305); + this.legalStuffTextBox.MaxLength = 0; + this.legalStuffTextBox.Multiline = true; + this.legalStuffTextBox.Name = "legalStuffTextBox"; + this.legalStuffTextBox.ReadOnly = true; + this.legalStuffTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.legalStuffTextBox.Size = new System.Drawing.Size(680, 215); + this.legalStuffTextBox.TabIndex = 4; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 289); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(59, 13); + this.label1.TabIndex = 5; + this.label1.Text = "Legal stuff:"; + // + // osPlatformLabel + // + this.osPlatformLabel.AutoSize = true; + this.osPlatformLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.osPlatformLabel.Location = new System.Drawing.Point(12, 263); + this.osPlatformLabel.Name = "osPlatformLabel"; + this.osPlatformLabel.Size = new System.Drawing.Size(86, 16); + this.osPlatformLabel.TabIndex = 6; + this.osPlatformLabel.Text = "[OS platform]"; + // + // debugEnabledLabel + // + this.debugEnabledLabel.AutoSize = true; + this.debugEnabledLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.debugEnabledLabel.Location = new System.Drawing.Point(340, 233); + this.debugEnabledLabel.Name = "debugEnabledLabel"; + this.debugEnabledLabel.Size = new System.Drawing.Size(293, 16); + this.debugEnabledLabel.TabIndex = 7; + this.debugEnabledLabel.Text = "Assertions and extended validation are enabled"; + this.debugEnabledLabel.Visible = false; + // + // AboutBox + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(704, 561); + this.Controls.Add(this.debugEnabledLabel); + this.Controls.Add(this.osPlatformLabel); + this.Controls.Add(this.label1); + this.Controls.Add(this.legalStuffTextBox); + this.Controls.Add(this.okButton); + this.Controls.Add(this.createdLabel); + this.Controls.Add(this.versionLabel); + this.Controls.Add(this.sourceGenLabel); + this.Controls.Add(this.boardPictureBox); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AboutBox"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "AboutBox"; + this.Load += new System.EventHandler(this.AboutBox_Load); + ((System.ComponentModel.ISupportInitialize)(this.boardPictureBox)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.PictureBox boardPictureBox; + private System.Windows.Forms.Label sourceGenLabel; + private System.Windows.Forms.Label versionLabel; + private System.Windows.Forms.Label createdLabel; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.TextBox legalStuffTextBox; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label osPlatformLabel; + private System.Windows.Forms.Label debugEnabledLabel; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/AboutBox.cs b/SourceGen/AppForms/AboutBox.cs new file mode 100644 index 0000000..3dc0ae5 --- /dev/null +++ b/SourceGen/AppForms/AboutBox.cs @@ -0,0 +1,51 @@ +/* + * Copyright 2018 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.IO; +using System.Windows.Forms; + +namespace SourceGen.AppForms { + public partial class AboutBox : Form { + private const string IMAGE_FILE_NAME = "AboutImage.png"; + private const string LEGAL_STUFF_FILE_NAME = "LegalStuff.txt"; + + public AboutBox() { + InitializeComponent(); + + boardPictureBox.ImageLocation = RuntimeDataAccess.GetPathName(IMAGE_FILE_NAME); + versionLabel.Text = string.Format(Properties.Resources.VERSION_FMT, + Program.ProgramVersion); + + osPlatformLabel.Text = "OS: " + + System.Runtime.InteropServices.RuntimeInformation.OSDescription; +#if DEBUG + debugEnabledLabel.Visible = true; +#endif + } + + private void AboutBox_Load(object sender, EventArgs e) { + string text; + string pathName = RuntimeDataAccess.GetPathName(LEGAL_STUFF_FILE_NAME); + try { + text = File.ReadAllText(pathName); + } catch (Exception ex) { + text = ex.ToString(); + } + + legalStuffTextBox.Text = text; + } + } +} diff --git a/SourceGen/AppForms/AboutBox.resx b/SourceGen/AppForms/AboutBox.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/AboutBox.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/DataFileLoadIssue.Designer.cs b/SourceGen/AppForms/DataFileLoadIssue.Designer.cs new file mode 100644 index 0000000..6ef16cd --- /dev/null +++ b/SourceGen/AppForms/DataFileLoadIssue.Designer.cs @@ -0,0 +1,143 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class DataFileLoadIssue { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.problemWithFileLabel = new System.Windows.Forms.Label(); + this.pathNameTextBox = new System.Windows.Forms.TextBox(); + this.problemLabel = new System.Windows.Forms.Label(); + this.cancelButton = new System.Windows.Forms.Button(); + this.okButton = new System.Windows.Forms.Button(); + this.doYouWantLabel = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // problemWithFileLabel + // + this.problemWithFileLabel.AutoSize = true; + this.problemWithFileLabel.Location = new System.Drawing.Point(13, 13); + this.problemWithFileLabel.Name = "problemWithFileLabel"; + this.problemWithFileLabel.Size = new System.Drawing.Size(221, 13); + this.problemWithFileLabel.TabIndex = 2; + this.problemWithFileLabel.Text = "There was an error while loading the data file:"; + // + // pathNameTextBox + // + this.pathNameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.pathNameTextBox.Location = new System.Drawing.Point(13, 39); + this.pathNameTextBox.Name = "pathNameTextBox"; + this.pathNameTextBox.ReadOnly = true; + this.pathNameTextBox.Size = new System.Drawing.Size(488, 20); + this.pathNameTextBox.TabIndex = 3; + // + // problemLabel + // + this.problemLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.problemLabel.Location = new System.Drawing.Point(13, 73); + this.problemLabel.Name = "problemLabel"; + this.problemLabel.Size = new System.Drawing.Size(488, 31); + this.problemLabel.TabIndex = 4; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(428, 113); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 1; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(347, 113); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 0; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + // + // doYouWantLabel + // + this.doYouWantLabel.AutoSize = true; + this.doYouWantLabel.Location = new System.Drawing.Point(13, 117); + this.doYouWantLabel.Name = "doYouWantLabel"; + this.doYouWantLabel.Size = new System.Drawing.Size(175, 13); + this.doYouWantLabel.TabIndex = 5; + this.doYouWantLabel.Text = "Do you want to locate the data file?\r\n"; + // + // DataFileLoadIssue + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(515, 148); + this.Controls.Add(this.doYouWantLabel); + this.Controls.Add(this.okButton); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.problemLabel); + this.Controls.Add(this.pathNameTextBox); + this.Controls.Add(this.problemWithFileLabel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "DataFileLoadIssue"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Data File Load Issue"; + this.Load += new System.EventHandler(this.DataFileLoadIssue_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label problemWithFileLabel; + private System.Windows.Forms.TextBox pathNameTextBox; + private System.Windows.Forms.Label problemLabel; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Label doYouWantLabel; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/DataFileLoadIssue.cs b/SourceGen/AppForms/DataFileLoadIssue.cs new file mode 100644 index 0000000..2cc1c0d --- /dev/null +++ b/SourceGen/AppForms/DataFileLoadIssue.cs @@ -0,0 +1,40 @@ +/* + * Copyright 2018 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.Windows.Forms; + +namespace SourceGen.AppForms { + public partial class DataFileLoadIssue : Form { + /// + /// Path name of problematic file. + /// + public string PathName { get; set; } + + /// + /// Message to show in the dialog. + /// + public string Message { get; set; } + + public DataFileLoadIssue() { + InitializeComponent(); + } + + private void DataFileLoadIssue_Load(object sender, EventArgs e) { + pathNameTextBox.Text = PathName; + problemLabel.Text = Message; + } + } +} diff --git a/SourceGen/AppForms/DataFileLoadIssue.resx b/SourceGen/AppForms/DataFileLoadIssue.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/DataFileLoadIssue.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/EditAddress.Designer.cs b/SourceGen/AppForms/EditAddress.Designer.cs new file mode 100644 index 0000000..70c9381 --- /dev/null +++ b/SourceGen/AppForms/EditAddress.Designer.cs @@ -0,0 +1,140 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class EditAddress { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + System.Windows.Forms.Label addressLabel; + this.okButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.instructionLabel1 = new System.Windows.Forms.Label(); + this.instructionLabel2 = new System.Windows.Forms.Label(); + addressLabel = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // addressLabel + // + addressLabel.AutoSize = true; + addressLabel.Location = new System.Drawing.Point(12, 16); + addressLabel.Name = "addressLabel"; + addressLabel.Size = new System.Drawing.Size(48, 13); + addressLabel.TabIndex = 1; + addressLabel.Text = "Address:"; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(190, 91); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 3; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(271, 91); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 4; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(68, 13); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(275, 20); + this.textBox1.TabIndex = 0; + // + // instructionLabel1 + // + this.instructionLabel1.AutoSize = true; + this.instructionLabel1.Location = new System.Drawing.Point(12, 36); + this.instructionLabel1.Name = "instructionLabel1"; + this.instructionLabel1.Size = new System.Drawing.Size(331, 13); + this.instructionLabel1.TabIndex = 2; + this.instructionLabel1.Text = "Enter 16-bit or 24-bit address in hexadecimal, e.g. $1000 or 00/be00.\r\n"; + // + // instructionLabel2 + // + this.instructionLabel2.AutoSize = true; + this.instructionLabel2.Location = new System.Drawing.Point(12, 58); + this.instructionLabel2.Name = "instructionLabel2"; + this.instructionLabel2.Size = new System.Drawing.Size(258, 13); + this.instructionLabel2.TabIndex = 5; + this.instructionLabel2.Text = "Leave the field blank to remove the address override."; + // + // EditAddress + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(358, 126); + this.Controls.Add(this.instructionLabel2); + this.Controls.Add(this.instructionLabel1); + this.Controls.Add(this.textBox1); + this.Controls.Add(addressLabel); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.okButton); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditAddress"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Address"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Label instructionLabel1; + private System.Windows.Forms.Label instructionLabel2; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/EditAddress.cs b/SourceGen/AppForms/EditAddress.cs new file mode 100644 index 0000000..7cca9db --- /dev/null +++ b/SourceGen/AppForms/EditAddress.cs @@ -0,0 +1,124 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Windows.Forms; + +namespace SourceGen.AppForms { + public partial class EditAddress : Form { +#if false + private bool mAllowLastChar; +#endif + + /// + /// Maximum allowed address value. + /// + public int MaxAddressValue { get; set; } + + /// + /// Address typed by user. Only valid after the dialog returns OK. Will be set to -1 + /// if the user is attempting to delete the address. + /// + public int Address { get; private set; } + + public EditAddress() { + InitializeComponent(); + Address = -2; + MaxAddressValue = (1 << 24) - 1; + +#if false + // This is probably not all that useful. We're not preventing + // invalid inputs, e.g. excessively large values or "$/$/$/", by restricting + // the keys that can be typed. + textBox1.KeyDown += textBox1_KeyDown; + textBox1.KeyPress += textBox1_KeyPress; +#endif + + // Update the OK button based on current contents. + textBox1.TextChanged += textBox1_TextChanged; + } + + public void SetInitialAddress(int addr) { + textBox1.Text = Asm65.Address.AddressToString(addr, false); + textBox1.SelectAll(); + } + + /// + /// Handles a click on the OK button by setting the Address property to the + /// decoded value from the text field. + /// + /// + /// + private void okButton_Click(object sender, EventArgs e) { + if (textBox1.Text.Length == 0) { + Address = -1; + } else { + Asm65.Address.ParseAddress(textBox1.Text, MaxAddressValue, out int addr); + Address = addr; + } + } + + /// + /// Enables or disables the OK button depending on whether the current input is + /// valid. We allow valid addresses and an empty box. + /// + private void UpdateOkEnabled() { + string text = textBox1.Text; + okButton.Enabled = (text.Length == 0) || + Asm65.Address.ParseAddress(text, MaxAddressValue, out int unused); + } + +#if false + /// + /// Limits characters to [A-F][a-f][0-9][/]. + /// + private void textBox1_KeyDown(object sender, KeyEventArgs e) { + bool allow = false; + if (e.KeyCode == Keys.D4 && e.Modifiers == Keys.Shift) { + allow = true; // allow '$'; not sure this works on non-US keyboards? + } else if (e.KeyCode >= Keys.A && e.KeyCode <= Keys.F) { + allow = !(e.Alt || e.Control); // allow shift + } else if ((e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9) || + e.KeyCode == Keys.OemQuestion) { + allow = (e.Modifiers == 0); + } else if (e.KeyCode == Keys.Back || e.KeyCode == Keys.Delete) { + allow = true; + } + + mAllowLastChar = allow; + //Debug.WriteLine("DOWN " + e.KeyCode + " allow=" + allow); + } + + /// + /// Rejects invalid characters. + /// + private void textBox1_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { + //Debug.WriteLine("PRESS " + e.KeyChar + " : " + mAllowLastChar); + if (!mAllowLastChar) { + e.Handled = true; + } + } +#endif + + /// + /// Updates the OK button whenever the text changes. This works for all change sources, + /// including programmatic. + /// + private void textBox1_TextChanged(object sender, EventArgs e) { + UpdateOkEnabled(); + } + } +} diff --git a/SourceGen/AppForms/EditAddress.resx b/SourceGen/AppForms/EditAddress.resx new file mode 100644 index 0000000..e38ed36 --- /dev/null +++ b/SourceGen/AppForms/EditAddress.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + \ No newline at end of file diff --git a/SourceGen/AppForms/EditAppSettings.Designer.cs b/SourceGen/AppForms/EditAppSettings.Designer.cs new file mode 100644 index 0000000..fc6a70b --- /dev/null +++ b/SourceGen/AppForms/EditAppSettings.Designer.cs @@ -0,0 +1,1583 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class EditAppSettings { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.cancelButton = new System.Windows.Forms.Button(); + this.okButton = new System.Windows.Forms.Button(); + this.applyButton = new System.Windows.Forms.Button(); + this.settingsTabControl = new System.Windows.Forms.TabControl(); + this.codeViewTabPage = new System.Windows.Forms.TabPage(); + this.clipboardGroupBox = new System.Windows.Forms.GroupBox(); + this.clipboardFormatLabel = new System.Windows.Forms.Label(); + this.clipboardFormatComboBox = new System.Windows.Forms.ComboBox(); + this.enableDebugCheckBox = new System.Windows.Forms.CheckBox(); + this.upperCaseGroupBox = new System.Windows.Forms.GroupBox(); + this.upperAllUpperButton = new System.Windows.Forms.Button(); + this.upperAllLowerButton = new System.Windows.Forms.Button(); + this.upperXYCheckBox = new System.Windows.Forms.CheckBox(); + this.upperSCheckBox = new System.Windows.Forms.CheckBox(); + this.upperACheckBox = new System.Windows.Forms.CheckBox(); + this.upperPseudoOpCheckBox = new System.Windows.Forms.CheckBox(); + this.upperOpcodeCheckBox = new System.Windows.Forms.CheckBox(); + this.upperHexCheckBox = new System.Windows.Forms.CheckBox(); + this.codeViewFontGroupBox = new System.Windows.Forms.GroupBox(); + this.currentFontLabel = new System.Windows.Forms.Label(); + this.selectFontButton = new System.Windows.Forms.Button(); + this.currentFontDisplayLabel = new System.Windows.Forms.Label(); + this.columnVisGroup = new System.Windows.Forms.GroupBox(); + this.showCol0 = new System.Windows.Forms.Button(); + this.showCol8 = new System.Windows.Forms.Button(); + this.showCol1 = new System.Windows.Forms.Button(); + this.showCol7 = new System.Windows.Forms.Button(); + this.showCol2 = new System.Windows.Forms.Button(); + this.showCol6 = new System.Windows.Forms.Button(); + this.showCol3 = new System.Windows.Forms.Button(); + this.showCol5 = new System.Windows.Forms.Button(); + this.showCol4 = new System.Windows.Forms.Button(); + this.asmConfigTabPage = new System.Windows.Forms.TabPage(); + this.showCycleCountsCheckBox = new System.Windows.Forms.CheckBox(); + this.configAsmGenLabel = new System.Windows.Forms.Label(); + this.longLabelNewLineCheckBox = new System.Windows.Forms.CheckBox(); + this.showAsmIdentCheckBox = new System.Windows.Forms.CheckBox(); + this.disableLabelLocalizationCheckBox = new System.Windows.Forms.CheckBox(); + this.clearMerlin32Button = new System.Windows.Forms.Button(); + this.clearCc65Button = new System.Windows.Forms.Button(); + this.browseMerlin32Button = new System.Windows.Forms.Button(); + this.browseCc65Button = new System.Windows.Forms.Button(); + this.cc65PathTextBox = new System.Windows.Forms.TextBox(); + this.merlin32PathTextBox = new System.Windows.Forms.TextBox(); + this.asmMerln32Label = new System.Windows.Forms.Label(); + this.asmCc65Label = new System.Windows.Forms.Label(); + this.asmExplanationLabel = new System.Windows.Forms.Label(); + this.displayFormatTabPage = new System.Windows.Forms.TabPage(); + this.fmtExplanationLabel = new System.Windows.Forms.Label(); + this.quickDisplayFormatGroup = new System.Windows.Forms.GroupBox(); + this.quickFmtMerlin32Button = new System.Windows.Forms.Button(); + this.quickFmtCc65Button = new System.Windows.Forms.Button(); + this.quickFmtDefaultButton = new System.Windows.Forms.Button(); + this.useMerlinExpressions = new System.Windows.Forms.CheckBox(); + this.operandWidthGroupBox = new System.Windows.Forms.GroupBox(); + this.disambPrefix24TextBox = new System.Windows.Forms.TextBox(); + this.disambPrefix16TextBox = new System.Windows.Forms.TextBox(); + this.disambPrefix24Label = new System.Windows.Forms.Label(); + this.disambPrefix16Label = new System.Windows.Forms.Label(); + this.disambOperandPrefixLabel = new System.Windows.Forms.Label(); + this.disambSuffix24Label = new System.Windows.Forms.Label(); + this.disambSuffix16Label = new System.Windows.Forms.Label(); + this.disambOpcodeSuffixLabel = new System.Windows.Forms.Label(); + this.disambSuffix24TextBox = new System.Windows.Forms.TextBox(); + this.disambSuffix16TextBox = new System.Windows.Forms.TextBox(); + this.pseudoOpTabPage = new System.Windows.Forms.TabPage(); + this.quickPseudoSetGroup = new System.Windows.Forms.GroupBox(); + this.quickPseudoMerlin32 = new System.Windows.Forms.Button(); + this.quickPseudoCc65Button = new System.Windows.Forms.Button(); + this.quickPseudoDefaultButton = new System.Windows.Forms.Button(); + this.strDciHiTextBox = new System.Windows.Forms.TextBox(); + this.strDciHiLabel = new System.Windows.Forms.Label(); + this.strDciTextBox = new System.Windows.Forms.TextBox(); + this.strDciLabel = new System.Windows.Forms.Label(); + this.strLen16HiTextBox = new System.Windows.Forms.TextBox(); + this.strLen16HiLabel = new System.Windows.Forms.Label(); + this.strLen16TextBox = new System.Windows.Forms.TextBox(); + this.strLen16Label = new System.Windows.Forms.Label(); + this.strReverseHiTextBox = new System.Windows.Forms.TextBox(); + this.strReverseHiLabel = new System.Windows.Forms.Label(); + this.strReverseTextBox = new System.Windows.Forms.TextBox(); + this.strReverseLabel = new System.Windows.Forms.Label(); + this.strNullTermHiTextBox = new System.Windows.Forms.TextBox(); + this.strNullTermHiLabel = new System.Windows.Forms.Label(); + this.strNullTermTextBox = new System.Windows.Forms.TextBox(); + this.strNullTermLabel = new System.Windows.Forms.Label(); + this.strLen8HiTextBox = new System.Windows.Forms.TextBox(); + this.strLen8HiLabel = new System.Windows.Forms.Label(); + this.strLen8TextBox = new System.Windows.Forms.TextBox(); + this.strLen8Label = new System.Windows.Forms.Label(); + this.strGenericHiTextBox = new System.Windows.Forms.TextBox(); + this.strGenericHiLabel = new System.Windows.Forms.Label(); + this.strGenericTextBox = new System.Windows.Forms.TextBox(); + this.strGenericLabel = new System.Windows.Forms.Label(); + this.popExplanationLabel = new System.Windows.Forms.Label(); + this.denseTextBox = new System.Windows.Forms.TextBox(); + this.denseLabel = new System.Windows.Forms.Label(); + this.fillTextBox = new System.Windows.Forms.TextBox(); + this.fillLabel = new System.Windows.Forms.Label(); + this.defineBigData2TextBox = new System.Windows.Forms.TextBox(); + this.defineBigData2Label = new System.Windows.Forms.Label(); + this.defineData4TextBox = new System.Windows.Forms.TextBox(); + this.defineData4Label = new System.Windows.Forms.Label(); + this.defineData3TextBox = new System.Windows.Forms.TextBox(); + this.defineData3Label = new System.Windows.Forms.Label(); + this.defineData2TextBox = new System.Windows.Forms.TextBox(); + this.defineData2Label = new System.Windows.Forms.Label(); + this.regWidthDirectiveTextBox = new System.Windows.Forms.TextBox(); + this.regWidthDirectiveLabel = new System.Windows.Forms.Label(); + this.orgDirectiveTextBox = new System.Windows.Forms.TextBox(); + this.orgDirectiveLabel = new System.Windows.Forms.Label(); + this.defineData1TextBox = new System.Windows.Forms.TextBox(); + this.defineData1Label = new System.Windows.Forms.Label(); + this.equDirectiveTextBox = new System.Windows.Forms.TextBox(); + this.equDirectiveLabel = new System.Windows.Forms.Label(); + this.settingsTabControl.SuspendLayout(); + this.codeViewTabPage.SuspendLayout(); + this.clipboardGroupBox.SuspendLayout(); + this.upperCaseGroupBox.SuspendLayout(); + this.codeViewFontGroupBox.SuspendLayout(); + this.columnVisGroup.SuspendLayout(); + this.asmConfigTabPage.SuspendLayout(); + this.displayFormatTabPage.SuspendLayout(); + this.quickDisplayFormatGroup.SuspendLayout(); + this.operandWidthGroupBox.SuspendLayout(); + this.pseudoOpTabPage.SuspendLayout(); + this.quickPseudoSetGroup.SuspendLayout(); + this.SuspendLayout(); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(537, 406); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 3; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(456, 406); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 2; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // applyButton + // + this.applyButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.applyButton.Location = new System.Drawing.Point(354, 406); + this.applyButton.Name = "applyButton"; + this.applyButton.Size = new System.Drawing.Size(75, 23); + this.applyButton.TabIndex = 1; + this.applyButton.Text = "Apply"; + this.applyButton.UseVisualStyleBackColor = true; + this.applyButton.Click += new System.EventHandler(this.applyButton_Click); + // + // settingsTabControl + // + this.settingsTabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.settingsTabControl.Controls.Add(this.codeViewTabPage); + this.settingsTabControl.Controls.Add(this.asmConfigTabPage); + this.settingsTabControl.Controls.Add(this.displayFormatTabPage); + this.settingsTabControl.Controls.Add(this.pseudoOpTabPage); + this.settingsTabControl.Location = new System.Drawing.Point(2, 2); + this.settingsTabControl.Name = "settingsTabControl"; + this.settingsTabControl.SelectedIndex = 0; + this.settingsTabControl.Size = new System.Drawing.Size(622, 398); + this.settingsTabControl.TabIndex = 0; + // + // codeViewTabPage + // + this.codeViewTabPage.Controls.Add(this.clipboardGroupBox); + this.codeViewTabPage.Controls.Add(this.enableDebugCheckBox); + this.codeViewTabPage.Controls.Add(this.upperCaseGroupBox); + this.codeViewTabPage.Controls.Add(this.codeViewFontGroupBox); + this.codeViewTabPage.Controls.Add(this.columnVisGroup); + this.codeViewTabPage.Location = new System.Drawing.Point(4, 22); + this.codeViewTabPage.Name = "codeViewTabPage"; + this.codeViewTabPage.Padding = new System.Windows.Forms.Padding(3); + this.codeViewTabPage.Size = new System.Drawing.Size(614, 372); + this.codeViewTabPage.TabIndex = 0; + this.codeViewTabPage.Text = "Code View"; + this.codeViewTabPage.UseVisualStyleBackColor = true; + // + // clipboardGroupBox + // + this.clipboardGroupBox.Controls.Add(this.clipboardFormatLabel); + this.clipboardGroupBox.Controls.Add(this.clipboardFormatComboBox); + this.clipboardGroupBox.Location = new System.Drawing.Point(406, 6); + this.clipboardGroupBox.Name = "clipboardGroupBox"; + this.clipboardGroupBox.Size = new System.Drawing.Size(200, 89); + this.clipboardGroupBox.TabIndex = 4; + this.clipboardGroupBox.TabStop = false; + this.clipboardGroupBox.Text = "Clipboard"; + // + // clipboardFormatLabel + // + this.clipboardFormatLabel.AutoSize = true; + this.clipboardFormatLabel.Location = new System.Drawing.Point(6, 22); + this.clipboardFormatLabel.Name = "clipboardFormatLabel"; + this.clipboardFormatLabel.Size = new System.Drawing.Size(174, 13); + this.clipboardFormatLabel.TabIndex = 1; + this.clipboardFormatLabel.Text = "Format for lines copied to clipboard:"; + // + // clipboardFormatComboBox + // + this.clipboardFormatComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.clipboardFormatComboBox.FormattingEnabled = true; + this.clipboardFormatComboBox.Items.AddRange(new object[] { + "Assembler Source", + "Disassembly"}); + this.clipboardFormatComboBox.Location = new System.Drawing.Point(6, 49); + this.clipboardFormatComboBox.Name = "clipboardFormatComboBox"; + this.clipboardFormatComboBox.Size = new System.Drawing.Size(188, 21); + this.clipboardFormatComboBox.TabIndex = 0; + this.clipboardFormatComboBox.SelectedIndexChanged += new System.EventHandler(this.clipboardFormatComboBox_SelectedIndexChanged); + // + // enableDebugCheckBox + // + this.enableDebugCheckBox.AutoSize = true; + this.enableDebugCheckBox.Location = new System.Drawing.Point(12, 349); + this.enableDebugCheckBox.Name = "enableDebugCheckBox"; + this.enableDebugCheckBox.Size = new System.Drawing.Size(129, 17); + this.enableDebugCheckBox.TabIndex = 3; + this.enableDebugCheckBox.Text = "Enable DEBUG menu"; + this.enableDebugCheckBox.UseVisualStyleBackColor = true; + this.enableDebugCheckBox.CheckedChanged += new System.EventHandler(this.enableDebugCheckBox_CheckedChanged); + // + // upperCaseGroupBox + // + this.upperCaseGroupBox.Controls.Add(this.upperAllUpperButton); + this.upperCaseGroupBox.Controls.Add(this.upperAllLowerButton); + this.upperCaseGroupBox.Controls.Add(this.upperXYCheckBox); + this.upperCaseGroupBox.Controls.Add(this.upperSCheckBox); + this.upperCaseGroupBox.Controls.Add(this.upperACheckBox); + this.upperCaseGroupBox.Controls.Add(this.upperPseudoOpCheckBox); + this.upperCaseGroupBox.Controls.Add(this.upperOpcodeCheckBox); + this.upperCaseGroupBox.Controls.Add(this.upperHexCheckBox); + this.upperCaseGroupBox.Location = new System.Drawing.Point(162, 101); + this.upperCaseGroupBox.Name = "upperCaseGroupBox"; + this.upperCaseGroupBox.Size = new System.Drawing.Size(224, 197); + this.upperCaseGroupBox.TabIndex = 0; + this.upperCaseGroupBox.TabStop = false; + this.upperCaseGroupBox.Text = "Upper Case Display"; + // + // upperAllUpperButton + // + this.upperAllUpperButton.Location = new System.Drawing.Point(88, 164); + this.upperAllUpperButton.Name = "upperAllUpperButton"; + this.upperAllUpperButton.Size = new System.Drawing.Size(75, 23); + this.upperAllUpperButton.TabIndex = 7; + this.upperAllUpperButton.Text = "All Upper"; + this.upperAllUpperButton.UseVisualStyleBackColor = true; + this.upperAllUpperButton.Click += new System.EventHandler(this.upperAllUpperButton_Click); + // + // upperAllLowerButton + // + this.upperAllLowerButton.Location = new System.Drawing.Point(7, 164); + this.upperAllLowerButton.Name = "upperAllLowerButton"; + this.upperAllLowerButton.Size = new System.Drawing.Size(75, 23); + this.upperAllLowerButton.TabIndex = 6; + this.upperAllLowerButton.Text = "All Lower"; + this.upperAllLowerButton.UseVisualStyleBackColor = true; + this.upperAllLowerButton.Click += new System.EventHandler(this.upperAllLowerButton_Click); + // + // upperXYCheckBox + // + this.upperXYCheckBox.AutoSize = true; + this.upperXYCheckBox.Location = new System.Drawing.Point(8, 139); + this.upperXYCheckBox.Name = "upperXYCheckBox"; + this.upperXYCheckBox.Size = new System.Drawing.Size(89, 17); + this.upperXYCheckBox.TabIndex = 5; + this.upperXYCheckBox.Text = "Operand X/Y"; + this.upperXYCheckBox.UseVisualStyleBackColor = true; + this.upperXYCheckBox.CheckedChanged += new System.EventHandler(this.upperXYCheckBox_CheckedChanged); + // + // upperSCheckBox + // + this.upperSCheckBox.AutoSize = true; + this.upperSCheckBox.Location = new System.Drawing.Point(8, 115); + this.upperSCheckBox.Name = "upperSCheckBox"; + this.upperSCheckBox.Size = new System.Drawing.Size(77, 17); + this.upperSCheckBox.TabIndex = 4; + this.upperSCheckBox.Text = "Operand S"; + this.upperSCheckBox.UseVisualStyleBackColor = true; + this.upperSCheckBox.CheckedChanged += new System.EventHandler(this.upperSCheckBox_CheckedChanged); + // + // upperACheckBox + // + this.upperACheckBox.AutoSize = true; + this.upperACheckBox.Location = new System.Drawing.Point(8, 91); + this.upperACheckBox.Name = "upperACheckBox"; + this.upperACheckBox.Size = new System.Drawing.Size(77, 17); + this.upperACheckBox.TabIndex = 3; + this.upperACheckBox.Text = "Operand A"; + this.upperACheckBox.UseVisualStyleBackColor = true; + this.upperACheckBox.CheckedChanged += new System.EventHandler(this.upperACheckBox_CheckedChanged); + // + // upperPseudoOpCheckBox + // + this.upperPseudoOpCheckBox.AutoSize = true; + this.upperPseudoOpCheckBox.Location = new System.Drawing.Point(8, 67); + this.upperPseudoOpCheckBox.Name = "upperPseudoOpCheckBox"; + this.upperPseudoOpCheckBox.Size = new System.Drawing.Size(106, 17); + this.upperPseudoOpCheckBox.TabIndex = 2; + this.upperPseudoOpCheckBox.Text = "Pseudo-opcodes"; + this.upperPseudoOpCheckBox.UseVisualStyleBackColor = true; + this.upperPseudoOpCheckBox.CheckedChanged += new System.EventHandler(this.upperPseudoOpCheckBox_CheckedChanged); + // + // upperOpcodeCheckBox + // + this.upperOpcodeCheckBox.AutoSize = true; + this.upperOpcodeCheckBox.Location = new System.Drawing.Point(8, 44); + this.upperOpcodeCheckBox.Name = "upperOpcodeCheckBox"; + this.upperOpcodeCheckBox.Size = new System.Drawing.Size(69, 17); + this.upperOpcodeCheckBox.TabIndex = 1; + this.upperOpcodeCheckBox.Text = "Opcodes"; + this.upperOpcodeCheckBox.UseVisualStyleBackColor = true; + this.upperOpcodeCheckBox.CheckedChanged += new System.EventHandler(this.upperOpcodeCheckBox_CheckedChanged); + // + // upperHexCheckBox + // + this.upperHexCheckBox.AutoSize = true; + this.upperHexCheckBox.Location = new System.Drawing.Point(8, 20); + this.upperHexCheckBox.Name = "upperHexCheckBox"; + this.upperHexCheckBox.Size = new System.Drawing.Size(121, 17); + this.upperHexCheckBox.TabIndex = 0; + this.upperHexCheckBox.Text = "Hexadecimal values"; + this.upperHexCheckBox.UseVisualStyleBackColor = true; + this.upperHexCheckBox.CheckedChanged += new System.EventHandler(this.upperHexCheckBox_CheckedChanged); + // + // codeViewFontGroupBox + // + this.codeViewFontGroupBox.Controls.Add(this.currentFontLabel); + this.codeViewFontGroupBox.Controls.Add(this.selectFontButton); + this.codeViewFontGroupBox.Controls.Add(this.currentFontDisplayLabel); + this.codeViewFontGroupBox.Location = new System.Drawing.Point(162, 6); + this.codeViewFontGroupBox.Name = "codeViewFontGroupBox"; + this.codeViewFontGroupBox.Size = new System.Drawing.Size(224, 89); + this.codeViewFontGroupBox.TabIndex = 2; + this.codeViewFontGroupBox.TabStop = false; + this.codeViewFontGroupBox.Text = "Code List Font"; + // + // currentFontLabel + // + this.currentFontLabel.AutoSize = true; + this.currentFontLabel.Location = new System.Drawing.Point(7, 18); + this.currentFontLabel.Name = "currentFontLabel"; + this.currentFontLabel.Size = new System.Drawing.Size(65, 13); + this.currentFontLabel.TabIndex = 0; + this.currentFontLabel.Text = "Current font:"; + // + // selectFontButton + // + this.selectFontButton.Location = new System.Drawing.Point(6, 60); + this.selectFontButton.Name = "selectFontButton"; + this.selectFontButton.Size = new System.Drawing.Size(125, 23); + this.selectFontButton.TabIndex = 2; + this.selectFontButton.Text = "Select Font..."; + this.selectFontButton.UseVisualStyleBackColor = true; + this.selectFontButton.Click += new System.EventHandler(this.selectFontButton_Click); + // + // currentFontDisplayLabel + // + this.currentFontDisplayLabel.AutoSize = true; + this.currentFontDisplayLabel.Location = new System.Drawing.Point(7, 33); + this.currentFontDisplayLabel.Name = "currentFontDisplayLabel"; + this.currentFontDisplayLabel.Size = new System.Drawing.Size(181, 13); + this.currentFontDisplayLabel.TabIndex = 1; + this.currentFontDisplayLabel.Text = "Constantia, 14.25pt, style=Bold, Italic"; + // + // columnVisGroup + // + this.columnVisGroup.Controls.Add(this.showCol0); + this.columnVisGroup.Controls.Add(this.showCol8); + this.columnVisGroup.Controls.Add(this.showCol1); + this.columnVisGroup.Controls.Add(this.showCol7); + this.columnVisGroup.Controls.Add(this.showCol2); + this.columnVisGroup.Controls.Add(this.showCol6); + this.columnVisGroup.Controls.Add(this.showCol3); + this.columnVisGroup.Controls.Add(this.showCol5); + this.columnVisGroup.Controls.Add(this.showCol4); + this.columnVisGroup.Location = new System.Drawing.Point(6, 6); + this.columnVisGroup.Name = "columnVisGroup"; + this.columnVisGroup.Size = new System.Drawing.Size(122, 292); + this.columnVisGroup.TabIndex = 0; + this.columnVisGroup.TabStop = false; + this.columnVisGroup.Text = "Column Visibility"; + // + // showCol0 + // + this.showCol0.Location = new System.Drawing.Point(6, 19); + this.showCol0.Name = "showCol0"; + this.showCol0.Size = new System.Drawing.Size(110, 23); + this.showCol0.TabIndex = 0; + this.showCol0.Text = "{0} Offset"; + this.showCol0.UseVisualStyleBackColor = true; + // + // showCol8 + // + this.showCol8.Location = new System.Drawing.Point(6, 259); + this.showCol8.Name = "showCol8"; + this.showCol8.Size = new System.Drawing.Size(110, 23); + this.showCol8.TabIndex = 8; + this.showCol8.Text = "{0} Comment"; + this.showCol8.UseVisualStyleBackColor = true; + // + // showCol1 + // + this.showCol1.Location = new System.Drawing.Point(6, 49); + this.showCol1.Name = "showCol1"; + this.showCol1.Size = new System.Drawing.Size(110, 23); + this.showCol1.TabIndex = 1; + this.showCol1.Text = "{0} Address"; + this.showCol1.UseVisualStyleBackColor = true; + // + // showCol7 + // + this.showCol7.Location = new System.Drawing.Point(6, 229); + this.showCol7.Name = "showCol7"; + this.showCol7.Size = new System.Drawing.Size(110, 23); + this.showCol7.TabIndex = 7; + this.showCol7.Text = "{0} Operand"; + this.showCol7.UseVisualStyleBackColor = true; + // + // showCol2 + // + this.showCol2.Location = new System.Drawing.Point(6, 79); + this.showCol2.Name = "showCol2"; + this.showCol2.Size = new System.Drawing.Size(110, 23); + this.showCol2.TabIndex = 2; + this.showCol2.Text = "{0} Bytes"; + this.showCol2.UseVisualStyleBackColor = true; + // + // showCol6 + // + this.showCol6.Location = new System.Drawing.Point(6, 199); + this.showCol6.Name = "showCol6"; + this.showCol6.Size = new System.Drawing.Size(110, 23); + this.showCol6.TabIndex = 6; + this.showCol6.Text = "{0} Opcode"; + this.showCol6.UseVisualStyleBackColor = true; + // + // showCol3 + // + this.showCol3.Location = new System.Drawing.Point(6, 109); + this.showCol3.Name = "showCol3"; + this.showCol3.Size = new System.Drawing.Size(110, 23); + this.showCol3.TabIndex = 3; + this.showCol3.Text = "{0} Flags"; + this.showCol3.UseVisualStyleBackColor = true; + // + // showCol5 + // + this.showCol5.Location = new System.Drawing.Point(6, 169); + this.showCol5.Name = "showCol5"; + this.showCol5.Size = new System.Drawing.Size(110, 23); + this.showCol5.TabIndex = 5; + this.showCol5.Text = "{0} Label"; + this.showCol5.UseVisualStyleBackColor = true; + // + // showCol4 + // + this.showCol4.Location = new System.Drawing.Point(6, 139); + this.showCol4.Name = "showCol4"; + this.showCol4.Size = new System.Drawing.Size(110, 23); + this.showCol4.TabIndex = 4; + this.showCol4.Text = "{0} Attributes"; + this.showCol4.UseVisualStyleBackColor = true; + // + // asmConfigTabPage + // + this.asmConfigTabPage.Controls.Add(this.showCycleCountsCheckBox); + this.asmConfigTabPage.Controls.Add(this.configAsmGenLabel); + this.asmConfigTabPage.Controls.Add(this.longLabelNewLineCheckBox); + this.asmConfigTabPage.Controls.Add(this.showAsmIdentCheckBox); + this.asmConfigTabPage.Controls.Add(this.disableLabelLocalizationCheckBox); + this.asmConfigTabPage.Controls.Add(this.clearMerlin32Button); + this.asmConfigTabPage.Controls.Add(this.clearCc65Button); + this.asmConfigTabPage.Controls.Add(this.browseMerlin32Button); + this.asmConfigTabPage.Controls.Add(this.browseCc65Button); + this.asmConfigTabPage.Controls.Add(this.cc65PathTextBox); + this.asmConfigTabPage.Controls.Add(this.merlin32PathTextBox); + this.asmConfigTabPage.Controls.Add(this.asmMerln32Label); + this.asmConfigTabPage.Controls.Add(this.asmCc65Label); + this.asmConfigTabPage.Controls.Add(this.asmExplanationLabel); + this.asmConfigTabPage.Location = new System.Drawing.Point(4, 22); + this.asmConfigTabPage.Name = "asmConfigTabPage"; + this.asmConfigTabPage.Padding = new System.Windows.Forms.Padding(3); + this.asmConfigTabPage.Size = new System.Drawing.Size(614, 372); + this.asmConfigTabPage.TabIndex = 1; + this.asmConfigTabPage.Text = "Asm Config"; + this.asmConfigTabPage.UseVisualStyleBackColor = true; + // + // showCycleCountsCheckBox + // + this.showCycleCountsCheckBox.AutoSize = true; + this.showCycleCountsCheckBox.Location = new System.Drawing.Point(10, 217); + this.showCycleCountsCheckBox.Name = "showCycleCountsCheckBox"; + this.showCycleCountsCheckBox.Size = new System.Drawing.Size(116, 17); + this.showCycleCountsCheckBox.TabIndex = 53; + this.showCycleCountsCheckBox.Text = "Show cycle counts"; + this.showCycleCountsCheckBox.UseVisualStyleBackColor = true; + this.showCycleCountsCheckBox.CheckedChanged += new System.EventHandler(this.showCycleCountsCheckBox_CheckedChanged); + // + // configAsmGenLabel + // + this.configAsmGenLabel.AutoSize = true; + this.configAsmGenLabel.Location = new System.Drawing.Point(7, 128); + this.configAsmGenLabel.Name = "configAsmGenLabel"; + this.configAsmGenLabel.Size = new System.Drawing.Size(154, 13); + this.configAsmGenLabel.TabIndex = 52; + this.configAsmGenLabel.Text = "Configure assembly generation:"; + // + // longLabelNewLineCheckBox + // + this.longLabelNewLineCheckBox.AutoSize = true; + this.longLabelNewLineCheckBox.Location = new System.Drawing.Point(10, 193); + this.longLabelNewLineCheckBox.Name = "longLabelNewLineCheckBox"; + this.longLabelNewLineCheckBox.Size = new System.Drawing.Size(173, 17); + this.longLabelNewLineCheckBox.TabIndex = 51; + this.longLabelNewLineCheckBox.Text = "Put long labels on separate line"; + this.longLabelNewLineCheckBox.UseVisualStyleBackColor = true; + // + // showAsmIdentCheckBox + // + this.showAsmIdentCheckBox.AutoSize = true; + this.showAsmIdentCheckBox.Location = new System.Drawing.Point(10, 147); + this.showAsmIdentCheckBox.Name = "showAsmIdentCheckBox"; + this.showAsmIdentCheckBox.Size = new System.Drawing.Size(154, 17); + this.showAsmIdentCheckBox.TabIndex = 10; + this.showAsmIdentCheckBox.Text = "Identify assembler in output"; + this.showAsmIdentCheckBox.UseVisualStyleBackColor = true; + this.showAsmIdentCheckBox.CheckedChanged += new System.EventHandler(this.showAsmIdentCheckBox_CheckedChanged); + // + // disableLabelLocalizationCheckBox + // + this.disableLabelLocalizationCheckBox.AutoSize = true; + this.disableLabelLocalizationCheckBox.Location = new System.Drawing.Point(10, 170); + this.disableLabelLocalizationCheckBox.Name = "disableLabelLocalizationCheckBox"; + this.disableLabelLocalizationCheckBox.Size = new System.Drawing.Size(141, 17); + this.disableLabelLocalizationCheckBox.TabIndex = 9; + this.disableLabelLocalizationCheckBox.Text = "Disable label localization"; + this.disableLabelLocalizationCheckBox.UseVisualStyleBackColor = true; + this.disableLabelLocalizationCheckBox.CheckedChanged += new System.EventHandler(this.disableLabelLocalizationCheckBox_CheckedChanged); + // + // clearMerlin32Button + // + this.clearMerlin32Button.Location = new System.Drawing.Point(531, 76); + this.clearMerlin32Button.Name = "clearMerlin32Button"; + this.clearMerlin32Button.Size = new System.Drawing.Size(75, 23); + this.clearMerlin32Button.TabIndex = 8; + this.clearMerlin32Button.Text = "Clear"; + this.clearMerlin32Button.UseVisualStyleBackColor = true; + this.clearMerlin32Button.Click += new System.EventHandler(this.clearMerlin32Button_Click); + // + // clearCc65Button + // + this.clearCc65Button.Location = new System.Drawing.Point(531, 35); + this.clearCc65Button.Name = "clearCc65Button"; + this.clearCc65Button.Size = new System.Drawing.Size(75, 23); + this.clearCc65Button.TabIndex = 4; + this.clearCc65Button.Text = "Clear"; + this.clearCc65Button.UseVisualStyleBackColor = true; + this.clearCc65Button.Click += new System.EventHandler(this.clearCc65Button_Click); + // + // browseMerlin32Button + // + this.browseMerlin32Button.Location = new System.Drawing.Point(450, 76); + this.browseMerlin32Button.Name = "browseMerlin32Button"; + this.browseMerlin32Button.Size = new System.Drawing.Size(75, 23); + this.browseMerlin32Button.TabIndex = 7; + this.browseMerlin32Button.Text = "Browse..."; + this.browseMerlin32Button.UseVisualStyleBackColor = true; + this.browseMerlin32Button.Click += new System.EventHandler(this.browseMerlin32Button_Click); + // + // browseCc65Button + // + this.browseCc65Button.Location = new System.Drawing.Point(450, 35); + this.browseCc65Button.Name = "browseCc65Button"; + this.browseCc65Button.Size = new System.Drawing.Size(75, 23); + this.browseCc65Button.TabIndex = 3; + this.browseCc65Button.Text = "Browse..."; + this.browseCc65Button.UseVisualStyleBackColor = true; + this.browseCc65Button.Click += new System.EventHandler(this.browseCc65Button_Click); + // + // cc65PathTextBox + // + this.cc65PathTextBox.Location = new System.Drawing.Point(65, 37); + this.cc65PathTextBox.Name = "cc65PathTextBox"; + this.cc65PathTextBox.Size = new System.Drawing.Size(379, 20); + this.cc65PathTextBox.TabIndex = 2; + this.cc65PathTextBox.Text = "C:\\something"; + this.cc65PathTextBox.TextChanged += new System.EventHandler(this.cc65PathTextBox_TextChanged); + // + // merlin32PathTextBox + // + this.merlin32PathTextBox.Location = new System.Drawing.Point(65, 78); + this.merlin32PathTextBox.Name = "merlin32PathTextBox"; + this.merlin32PathTextBox.Size = new System.Drawing.Size(379, 20); + this.merlin32PathTextBox.TabIndex = 6; + this.merlin32PathTextBox.Text = "C:\\something"; + this.merlin32PathTextBox.TextChanged += new System.EventHandler(this.merlin32PathTextBox_TextChanged); + // + // asmMerln32Label + // + this.asmMerln32Label.AutoSize = true; + this.asmMerln32Label.Location = new System.Drawing.Point(6, 81); + this.asmMerln32Label.Name = "asmMerln32Label"; + this.asmMerln32Label.Size = new System.Drawing.Size(53, 13); + this.asmMerln32Label.TabIndex = 5; + this.asmMerln32Label.Text = "Merlin 32:"; + // + // asmCc65Label + // + this.asmCc65Label.AutoSize = true; + this.asmCc65Label.Location = new System.Drawing.Point(25, 40); + this.asmCc65Label.Name = "asmCc65Label"; + this.asmCc65Label.Size = new System.Drawing.Size(34, 13); + this.asmCc65Label.TabIndex = 1; + this.asmCc65Label.Text = "cc65:"; + // + // asmExplanationLabel + // + this.asmExplanationLabel.AutoSize = true; + this.asmExplanationLabel.Location = new System.Drawing.Point(7, 7); + this.asmExplanationLabel.Name = "asmExplanationLabel"; + this.asmExplanationLabel.Size = new System.Drawing.Size(179, 13); + this.asmExplanationLabel.TabIndex = 0; + this.asmExplanationLabel.Text = "Configure installed cross-assemblers:"; + // + // displayFormatTabPage + // + this.displayFormatTabPage.Controls.Add(this.fmtExplanationLabel); + this.displayFormatTabPage.Controls.Add(this.quickDisplayFormatGroup); + this.displayFormatTabPage.Controls.Add(this.useMerlinExpressions); + this.displayFormatTabPage.Controls.Add(this.operandWidthGroupBox); + this.displayFormatTabPage.Location = new System.Drawing.Point(4, 22); + this.displayFormatTabPage.Name = "displayFormatTabPage"; + this.displayFormatTabPage.Padding = new System.Windows.Forms.Padding(3); + this.displayFormatTabPage.Size = new System.Drawing.Size(614, 372); + this.displayFormatTabPage.TabIndex = 3; + this.displayFormatTabPage.Text = "Display Format"; + this.displayFormatTabPage.UseVisualStyleBackColor = true; + // + // fmtExplanationLabel + // + this.fmtExplanationLabel.AutoSize = true; + this.fmtExplanationLabel.Location = new System.Drawing.Point(7, 7); + this.fmtExplanationLabel.Name = "fmtExplanationLabel"; + this.fmtExplanationLabel.Size = new System.Drawing.Size(374, 13); + this.fmtExplanationLabel.TabIndex = 52; + this.fmtExplanationLabel.Text = "Configure display format options. This does not affect source code generation."; + // + // quickDisplayFormatGroup + // + this.quickDisplayFormatGroup.Controls.Add(this.quickFmtMerlin32Button); + this.quickDisplayFormatGroup.Controls.Add(this.quickFmtCc65Button); + this.quickDisplayFormatGroup.Controls.Add(this.quickFmtDefaultButton); + this.quickDisplayFormatGroup.Location = new System.Drawing.Point(348, 291); + this.quickDisplayFormatGroup.Name = "quickDisplayFormatGroup"; + this.quickDisplayFormatGroup.Size = new System.Drawing.Size(258, 75); + this.quickDisplayFormatGroup.TabIndex = 51; + this.quickDisplayFormatGroup.TabStop = false; + this.quickDisplayFormatGroup.Text = "Quick Set"; + // + // quickFmtMerlin32Button + // + this.quickFmtMerlin32Button.Location = new System.Drawing.Point(173, 30); + this.quickFmtMerlin32Button.Name = "quickFmtMerlin32Button"; + this.quickFmtMerlin32Button.Size = new System.Drawing.Size(75, 23); + this.quickFmtMerlin32Button.TabIndex = 2; + this.quickFmtMerlin32Button.Text = "Merlin 32"; + this.quickFmtMerlin32Button.UseVisualStyleBackColor = true; + this.quickFmtMerlin32Button.Click += new System.EventHandler(this.quickFmtMerlin32Button_Click); + // + // quickFmtCc65Button + // + this.quickFmtCc65Button.Location = new System.Drawing.Point(92, 30); + this.quickFmtCc65Button.Name = "quickFmtCc65Button"; + this.quickFmtCc65Button.Size = new System.Drawing.Size(75, 23); + this.quickFmtCc65Button.TabIndex = 1; + this.quickFmtCc65Button.Text = "cc65"; + this.quickFmtCc65Button.UseVisualStyleBackColor = true; + this.quickFmtCc65Button.Click += new System.EventHandler(this.quickFmtCc65Button_Click); + // + // quickFmtDefaultButton + // + this.quickFmtDefaultButton.Location = new System.Drawing.Point(11, 30); + this.quickFmtDefaultButton.Name = "quickFmtDefaultButton"; + this.quickFmtDefaultButton.Size = new System.Drawing.Size(75, 23); + this.quickFmtDefaultButton.TabIndex = 0; + this.quickFmtDefaultButton.Text = "Default"; + this.quickFmtDefaultButton.UseVisualStyleBackColor = true; + this.quickFmtDefaultButton.Click += new System.EventHandler(this.quickFmtDefaultButton_Click); + // + // useMerlinExpressions + // + this.useMerlinExpressions.AutoSize = true; + this.useMerlinExpressions.Location = new System.Drawing.Point(6, 153); + this.useMerlinExpressions.Name = "useMerlinExpressions"; + this.useMerlinExpressions.Size = new System.Drawing.Size(158, 17); + this.useMerlinExpressions.TabIndex = 49; + this.useMerlinExpressions.Text = "Use Merlin-style expressions"; + this.useMerlinExpressions.UseVisualStyleBackColor = true; + this.useMerlinExpressions.CheckedChanged += new System.EventHandler(this.shiftAfterAdjustCheckBox_CheckedChanged); + // + // operandWidthGroupBox + // + this.operandWidthGroupBox.Controls.Add(this.disambPrefix24TextBox); + this.operandWidthGroupBox.Controls.Add(this.disambPrefix16TextBox); + this.operandWidthGroupBox.Controls.Add(this.disambPrefix24Label); + this.operandWidthGroupBox.Controls.Add(this.disambPrefix16Label); + this.operandWidthGroupBox.Controls.Add(this.disambOperandPrefixLabel); + this.operandWidthGroupBox.Controls.Add(this.disambSuffix24Label); + this.operandWidthGroupBox.Controls.Add(this.disambSuffix16Label); + this.operandWidthGroupBox.Controls.Add(this.disambOpcodeSuffixLabel); + this.operandWidthGroupBox.Controls.Add(this.disambSuffix24TextBox); + this.operandWidthGroupBox.Controls.Add(this.disambSuffix16TextBox); + this.operandWidthGroupBox.Location = new System.Drawing.Point(6, 38); + this.operandWidthGroupBox.Name = "operandWidthGroupBox"; + this.operandWidthGroupBox.Size = new System.Drawing.Size(295, 98); + this.operandWidthGroupBox.TabIndex = 48; + this.operandWidthGroupBox.TabStop = false; + this.operandWidthGroupBox.Text = "Operand Width Disambiguator"; + // + // disambPrefix24TextBox + // + this.disambPrefix24TextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.disambPrefix24TextBox.Location = new System.Drawing.Point(220, 64); + this.disambPrefix24TextBox.MaxLength = 8; + this.disambPrefix24TextBox.Name = "disambPrefix24TextBox"; + this.disambPrefix24TextBox.Size = new System.Drawing.Size(62, 20); + this.disambPrefix24TextBox.TabIndex = 13; + this.disambPrefix24TextBox.Text = ".placeho"; + this.disambPrefix24TextBox.TextChanged += new System.EventHandler(this.WidthDisamControlChanged); + // + // disambPrefix16TextBox + // + this.disambPrefix16TextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.disambPrefix16TextBox.Location = new System.Drawing.Point(220, 38); + this.disambPrefix16TextBox.MaxLength = 8; + this.disambPrefix16TextBox.Name = "disambPrefix16TextBox"; + this.disambPrefix16TextBox.Size = new System.Drawing.Size(62, 20); + this.disambPrefix16TextBox.TabIndex = 12; + this.disambPrefix16TextBox.Text = ".placeho"; + this.disambPrefix16TextBox.TextChanged += new System.EventHandler(this.WidthDisamControlChanged); + // + // disambPrefix24Label + // + this.disambPrefix24Label.AutoSize = true; + this.disambPrefix24Label.Location = new System.Drawing.Point(171, 66); + this.disambPrefix24Label.Name = "disambPrefix24Label"; + this.disambPrefix24Label.Size = new System.Drawing.Size(36, 13); + this.disambPrefix24Label.TabIndex = 11; + this.disambPrefix24Label.Text = "24 bit:"; + // + // disambPrefix16Label + // + this.disambPrefix16Label.AutoSize = true; + this.disambPrefix16Label.Location = new System.Drawing.Point(171, 40); + this.disambPrefix16Label.Name = "disambPrefix16Label"; + this.disambPrefix16Label.Size = new System.Drawing.Size(36, 13); + this.disambPrefix16Label.TabIndex = 10; + this.disambPrefix16Label.Text = "16 bit:"; + // + // disambOperandPrefixLabel + // + this.disambOperandPrefixLabel.AutoSize = true; + this.disambOperandPrefixLabel.Location = new System.Drawing.Point(171, 20); + this.disambOperandPrefixLabel.Name = "disambOperandPrefixLabel"; + this.disambOperandPrefixLabel.Size = new System.Drawing.Size(79, 13); + this.disambOperandPrefixLabel.TabIndex = 9; + this.disambOperandPrefixLabel.Text = "Operand prefix:"; + // + // disambSuffix24Label + // + this.disambSuffix24Label.AutoSize = true; + this.disambSuffix24Label.Location = new System.Drawing.Point(10, 66); + this.disambSuffix24Label.Name = "disambSuffix24Label"; + this.disambSuffix24Label.Size = new System.Drawing.Size(36, 13); + this.disambSuffix24Label.TabIndex = 8; + this.disambSuffix24Label.Text = "24 bit:"; + // + // disambSuffix16Label + // + this.disambSuffix16Label.AutoSize = true; + this.disambSuffix16Label.Location = new System.Drawing.Point(10, 40); + this.disambSuffix16Label.Name = "disambSuffix16Label"; + this.disambSuffix16Label.Size = new System.Drawing.Size(36, 13); + this.disambSuffix16Label.TabIndex = 7; + this.disambSuffix16Label.Text = "16 bit:"; + // + // disambOpcodeSuffixLabel + // + this.disambOpcodeSuffixLabel.AutoSize = true; + this.disambOpcodeSuffixLabel.Location = new System.Drawing.Point(7, 20); + this.disambOpcodeSuffixLabel.Name = "disambOpcodeSuffixLabel"; + this.disambOpcodeSuffixLabel.Size = new System.Drawing.Size(75, 13); + this.disambOpcodeSuffixLabel.TabIndex = 6; + this.disambOpcodeSuffixLabel.Text = "Opcode suffix:"; + // + // disambSuffix24TextBox + // + this.disambSuffix24TextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.disambSuffix24TextBox.Location = new System.Drawing.Point(62, 64); + this.disambSuffix24TextBox.MaxLength = 8; + this.disambSuffix24TextBox.Name = "disambSuffix24TextBox"; + this.disambSuffix24TextBox.Size = new System.Drawing.Size(62, 20); + this.disambSuffix24TextBox.TabIndex = 5; + this.disambSuffix24TextBox.Text = ".placeho"; + this.disambSuffix24TextBox.TextChanged += new System.EventHandler(this.WidthDisamControlChanged); + // + // disambSuffix16TextBox + // + this.disambSuffix16TextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.disambSuffix16TextBox.Location = new System.Drawing.Point(62, 38); + this.disambSuffix16TextBox.MaxLength = 8; + this.disambSuffix16TextBox.Name = "disambSuffix16TextBox"; + this.disambSuffix16TextBox.Size = new System.Drawing.Size(62, 20); + this.disambSuffix16TextBox.TabIndex = 3; + this.disambSuffix16TextBox.Text = ".placeho"; + this.disambSuffix16TextBox.TextChanged += new System.EventHandler(this.WidthDisamControlChanged); + // + // pseudoOpTabPage + // + this.pseudoOpTabPage.Controls.Add(this.quickPseudoSetGroup); + this.pseudoOpTabPage.Controls.Add(this.strDciHiTextBox); + this.pseudoOpTabPage.Controls.Add(this.strDciHiLabel); + this.pseudoOpTabPage.Controls.Add(this.strDciTextBox); + this.pseudoOpTabPage.Controls.Add(this.strDciLabel); + this.pseudoOpTabPage.Controls.Add(this.strLen16HiTextBox); + this.pseudoOpTabPage.Controls.Add(this.strLen16HiLabel); + this.pseudoOpTabPage.Controls.Add(this.strLen16TextBox); + this.pseudoOpTabPage.Controls.Add(this.strLen16Label); + this.pseudoOpTabPage.Controls.Add(this.strReverseHiTextBox); + this.pseudoOpTabPage.Controls.Add(this.strReverseHiLabel); + this.pseudoOpTabPage.Controls.Add(this.strReverseTextBox); + this.pseudoOpTabPage.Controls.Add(this.strReverseLabel); + this.pseudoOpTabPage.Controls.Add(this.strNullTermHiTextBox); + this.pseudoOpTabPage.Controls.Add(this.strNullTermHiLabel); + this.pseudoOpTabPage.Controls.Add(this.strNullTermTextBox); + this.pseudoOpTabPage.Controls.Add(this.strNullTermLabel); + this.pseudoOpTabPage.Controls.Add(this.strLen8HiTextBox); + this.pseudoOpTabPage.Controls.Add(this.strLen8HiLabel); + this.pseudoOpTabPage.Controls.Add(this.strLen8TextBox); + this.pseudoOpTabPage.Controls.Add(this.strLen8Label); + this.pseudoOpTabPage.Controls.Add(this.strGenericHiTextBox); + this.pseudoOpTabPage.Controls.Add(this.strGenericHiLabel); + this.pseudoOpTabPage.Controls.Add(this.strGenericTextBox); + this.pseudoOpTabPage.Controls.Add(this.strGenericLabel); + this.pseudoOpTabPage.Controls.Add(this.popExplanationLabel); + this.pseudoOpTabPage.Controls.Add(this.denseTextBox); + this.pseudoOpTabPage.Controls.Add(this.denseLabel); + this.pseudoOpTabPage.Controls.Add(this.fillTextBox); + this.pseudoOpTabPage.Controls.Add(this.fillLabel); + this.pseudoOpTabPage.Controls.Add(this.defineBigData2TextBox); + this.pseudoOpTabPage.Controls.Add(this.defineBigData2Label); + this.pseudoOpTabPage.Controls.Add(this.defineData4TextBox); + this.pseudoOpTabPage.Controls.Add(this.defineData4Label); + this.pseudoOpTabPage.Controls.Add(this.defineData3TextBox); + this.pseudoOpTabPage.Controls.Add(this.defineData3Label); + this.pseudoOpTabPage.Controls.Add(this.defineData2TextBox); + this.pseudoOpTabPage.Controls.Add(this.defineData2Label); + this.pseudoOpTabPage.Controls.Add(this.regWidthDirectiveTextBox); + this.pseudoOpTabPage.Controls.Add(this.regWidthDirectiveLabel); + this.pseudoOpTabPage.Controls.Add(this.orgDirectiveTextBox); + this.pseudoOpTabPage.Controls.Add(this.orgDirectiveLabel); + this.pseudoOpTabPage.Controls.Add(this.defineData1TextBox); + this.pseudoOpTabPage.Controls.Add(this.defineData1Label); + this.pseudoOpTabPage.Controls.Add(this.equDirectiveTextBox); + this.pseudoOpTabPage.Controls.Add(this.equDirectiveLabel); + this.pseudoOpTabPage.Location = new System.Drawing.Point(4, 22); + this.pseudoOpTabPage.Name = "pseudoOpTabPage"; + this.pseudoOpTabPage.Padding = new System.Windows.Forms.Padding(3); + this.pseudoOpTabPage.Size = new System.Drawing.Size(614, 372); + this.pseudoOpTabPage.TabIndex = 2; + this.pseudoOpTabPage.Text = "Pseudo-Op"; + this.pseudoOpTabPage.UseVisualStyleBackColor = true; + // + // quickPseudoSetGroup + // + this.quickPseudoSetGroup.Controls.Add(this.quickPseudoMerlin32); + this.quickPseudoSetGroup.Controls.Add(this.quickPseudoCc65Button); + this.quickPseudoSetGroup.Controls.Add(this.quickPseudoDefaultButton); + this.quickPseudoSetGroup.Location = new System.Drawing.Point(348, 291); + this.quickPseudoSetGroup.Name = "quickPseudoSetGroup"; + this.quickPseudoSetGroup.Size = new System.Drawing.Size(258, 75); + this.quickPseudoSetGroup.TabIndex = 46; + this.quickPseudoSetGroup.TabStop = false; + this.quickPseudoSetGroup.Text = "Quick Set"; + // + // quickPseudoMerlin32 + // + this.quickPseudoMerlin32.Location = new System.Drawing.Point(173, 30); + this.quickPseudoMerlin32.Name = "quickPseudoMerlin32"; + this.quickPseudoMerlin32.Size = new System.Drawing.Size(75, 23); + this.quickPseudoMerlin32.TabIndex = 2; + this.quickPseudoMerlin32.Text = "Merlin 32"; + this.quickPseudoMerlin32.UseVisualStyleBackColor = true; + this.quickPseudoMerlin32.Click += new System.EventHandler(this.quickPseudoMerlin32_Click); + // + // quickPseudoCc65Button + // + this.quickPseudoCc65Button.Location = new System.Drawing.Point(92, 30); + this.quickPseudoCc65Button.Name = "quickPseudoCc65Button"; + this.quickPseudoCc65Button.Size = new System.Drawing.Size(75, 23); + this.quickPseudoCc65Button.TabIndex = 1; + this.quickPseudoCc65Button.Text = "cc65"; + this.quickPseudoCc65Button.UseVisualStyleBackColor = true; + this.quickPseudoCc65Button.Click += new System.EventHandler(this.quickPseudoCc65Button_Click); + // + // quickPseudoDefaultButton + // + this.quickPseudoDefaultButton.Location = new System.Drawing.Point(11, 30); + this.quickPseudoDefaultButton.Name = "quickPseudoDefaultButton"; + this.quickPseudoDefaultButton.Size = new System.Drawing.Size(75, 23); + this.quickPseudoDefaultButton.TabIndex = 0; + this.quickPseudoDefaultButton.Text = "Default"; + this.quickPseudoDefaultButton.UseVisualStyleBackColor = true; + this.quickPseudoDefaultButton.Click += new System.EventHandler(this.quickPseudoDefaultButton_Click); + // + // strDciHiTextBox + // + this.strDciHiTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.strDciHiTextBox.Location = new System.Drawing.Point(546, 230); + this.strDciHiTextBox.MaxLength = 8; + this.strDciHiTextBox.Name = "strDciHiTextBox"; + this.strDciHiTextBox.Size = new System.Drawing.Size(62, 20); + this.strDciHiTextBox.TabIndex = 44; + this.strDciHiTextBox.Text = ".placeho"; + this.strDciHiTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // strDciHiLabel + // + this.strDciHiLabel.Location = new System.Drawing.Point(463, 232); + this.strDciHiLabel.Name = "strDciHiLabel"; + this.strDciHiLabel.Size = new System.Drawing.Size(79, 23); + this.strDciHiLabel.TabIndex = 43; + this.strDciHiLabel.Text = "DCI/hi:"; + this.strDciHiLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // strDciTextBox + // + this.strDciTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.strDciTextBox.Location = new System.Drawing.Point(394, 230); + this.strDciTextBox.MaxLength = 8; + this.strDciTextBox.Name = "strDciTextBox"; + this.strDciTextBox.Size = new System.Drawing.Size(62, 20); + this.strDciTextBox.TabIndex = 42; + this.strDciTextBox.Text = ".placeho"; + this.strDciTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // strDciLabel + // + this.strDciLabel.Location = new System.Drawing.Point(309, 232); + this.strDciLabel.Name = "strDciLabel"; + this.strDciLabel.Size = new System.Drawing.Size(82, 23); + this.strDciLabel.TabIndex = 41; + this.strDciLabel.Text = "DCI:"; + this.strDciLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // strLen16HiTextBox + // + this.strLen16HiTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.strLen16HiTextBox.Location = new System.Drawing.Point(546, 192); + this.strLen16HiTextBox.MaxLength = 8; + this.strLen16HiTextBox.Name = "strLen16HiTextBox"; + this.strLen16HiTextBox.Size = new System.Drawing.Size(62, 20); + this.strLen16HiTextBox.TabIndex = 36; + this.strLen16HiTextBox.Text = ".placeho"; + this.strLen16HiTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // strLen16HiLabel + // + this.strLen16HiLabel.Location = new System.Drawing.Point(463, 194); + this.strLen16HiLabel.Name = "strLen16HiLabel"; + this.strLen16HiLabel.Size = new System.Drawing.Size(79, 23); + this.strLen16HiLabel.TabIndex = 35; + this.strLen16HiLabel.Text = "2-byte len/hi:"; + this.strLen16HiLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // strLen16TextBox + // + this.strLen16TextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.strLen16TextBox.Location = new System.Drawing.Point(394, 192); + this.strLen16TextBox.MaxLength = 8; + this.strLen16TextBox.Name = "strLen16TextBox"; + this.strLen16TextBox.Size = new System.Drawing.Size(62, 20); + this.strLen16TextBox.TabIndex = 34; + this.strLen16TextBox.Text = ".placeho"; + this.strLen16TextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // strLen16Label + // + this.strLen16Label.Location = new System.Drawing.Point(309, 194); + this.strLen16Label.Name = "strLen16Label"; + this.strLen16Label.Size = new System.Drawing.Size(82, 23); + this.strLen16Label.TabIndex = 33; + this.strLen16Label.Text = "2-byte len:"; + this.strLen16Label.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // strReverseHiTextBox + // + this.strReverseHiTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.strReverseHiTextBox.Location = new System.Drawing.Point(546, 154); + this.strReverseHiTextBox.MaxLength = 8; + this.strReverseHiTextBox.Name = "strReverseHiTextBox"; + this.strReverseHiTextBox.Size = new System.Drawing.Size(62, 20); + this.strReverseHiTextBox.TabIndex = 28; + this.strReverseHiTextBox.Text = ".placeho"; + this.strReverseHiTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // strReverseHiLabel + // + this.strReverseHiLabel.Location = new System.Drawing.Point(463, 156); + this.strReverseHiLabel.Name = "strReverseHiLabel"; + this.strReverseHiLabel.Size = new System.Drawing.Size(79, 23); + this.strReverseHiLabel.TabIndex = 27; + this.strReverseHiLabel.Text = "Reverse/hi:"; + this.strReverseHiLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // strReverseTextBox + // + this.strReverseTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.strReverseTextBox.Location = new System.Drawing.Point(394, 154); + this.strReverseTextBox.MaxLength = 8; + this.strReverseTextBox.Name = "strReverseTextBox"; + this.strReverseTextBox.Size = new System.Drawing.Size(62, 20); + this.strReverseTextBox.TabIndex = 26; + this.strReverseTextBox.Text = ".placeho"; + this.strReverseTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // strReverseLabel + // + this.strReverseLabel.Location = new System.Drawing.Point(309, 156); + this.strReverseLabel.Name = "strReverseLabel"; + this.strReverseLabel.Size = new System.Drawing.Size(82, 23); + this.strReverseLabel.TabIndex = 25; + this.strReverseLabel.Text = "Reverse:"; + this.strReverseLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // strNullTermHiTextBox + // + this.strNullTermHiTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.strNullTermHiTextBox.Location = new System.Drawing.Point(244, 230); + this.strNullTermHiTextBox.MaxLength = 8; + this.strNullTermHiTextBox.Name = "strNullTermHiTextBox"; + this.strNullTermHiTextBox.Size = new System.Drawing.Size(62, 20); + this.strNullTermHiTextBox.TabIndex = 40; + this.strNullTermHiTextBox.Text = ".placeho"; + this.strNullTermHiTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // strNullTermHiLabel + // + this.strNullTermHiLabel.Location = new System.Drawing.Point(160, 232); + this.strNullTermHiLabel.Name = "strNullTermHiLabel"; + this.strNullTermHiLabel.Size = new System.Drawing.Size(80, 23); + this.strNullTermHiLabel.TabIndex = 39; + this.strNullTermHiLabel.Text = "Null term/hi:"; + this.strNullTermHiLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // strNullTermTextBox + // + this.strNullTermTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.strNullTermTextBox.Location = new System.Drawing.Point(92, 230); + this.strNullTermTextBox.MaxLength = 8; + this.strNullTermTextBox.Name = "strNullTermTextBox"; + this.strNullTermTextBox.Size = new System.Drawing.Size(62, 20); + this.strNullTermTextBox.TabIndex = 38; + this.strNullTermTextBox.Text = ".placeho"; + this.strNullTermTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // strNullTermLabel + // + this.strNullTermLabel.Location = new System.Drawing.Point(3, 232); + this.strNullTermLabel.Name = "strNullTermLabel"; + this.strNullTermLabel.Size = new System.Drawing.Size(85, 23); + this.strNullTermLabel.TabIndex = 37; + this.strNullTermLabel.Text = "Null term string:"; + this.strNullTermLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // strLen8HiTextBox + // + this.strLen8HiTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.strLen8HiTextBox.Location = new System.Drawing.Point(244, 192); + this.strLen8HiTextBox.MaxLength = 8; + this.strLen8HiTextBox.Name = "strLen8HiTextBox"; + this.strLen8HiTextBox.Size = new System.Drawing.Size(62, 20); + this.strLen8HiTextBox.TabIndex = 32; + this.strLen8HiTextBox.Text = ".placeho"; + this.strLen8HiTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // strLen8HiLabel + // + this.strLen8HiLabel.Location = new System.Drawing.Point(160, 194); + this.strLen8HiLabel.Name = "strLen8HiLabel"; + this.strLen8HiLabel.Size = new System.Drawing.Size(80, 23); + this.strLen8HiLabel.TabIndex = 31; + this.strLen8HiLabel.Text = "1-byte len/hi:"; + this.strLen8HiLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // strLen8TextBox + // + this.strLen8TextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.strLen8TextBox.Location = new System.Drawing.Point(92, 192); + this.strLen8TextBox.MaxLength = 8; + this.strLen8TextBox.Name = "strLen8TextBox"; + this.strLen8TextBox.Size = new System.Drawing.Size(62, 20); + this.strLen8TextBox.TabIndex = 30; + this.strLen8TextBox.Text = ".placeho"; + this.strLen8TextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // strLen8Label + // + this.strLen8Label.Location = new System.Drawing.Point(7, 194); + this.strLen8Label.Name = "strLen8Label"; + this.strLen8Label.Size = new System.Drawing.Size(81, 23); + this.strLen8Label.TabIndex = 29; + this.strLen8Label.Text = "1-byte len:"; + this.strLen8Label.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // strGenericHiTextBox + // + this.strGenericHiTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.strGenericHiTextBox.Location = new System.Drawing.Point(244, 154); + this.strGenericHiTextBox.MaxLength = 8; + this.strGenericHiTextBox.Name = "strGenericHiTextBox"; + this.strGenericHiTextBox.Size = new System.Drawing.Size(62, 20); + this.strGenericHiTextBox.TabIndex = 24; + this.strGenericHiTextBox.Text = ".placeho"; + this.strGenericHiTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // strGenericHiLabel + // + this.strGenericHiLabel.Location = new System.Drawing.Point(160, 156); + this.strGenericHiLabel.Name = "strGenericHiLabel"; + this.strGenericHiLabel.Size = new System.Drawing.Size(80, 23); + this.strGenericHiLabel.TabIndex = 23; + this.strGenericHiLabel.Text = "Generic/hi:"; + this.strGenericHiLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // strGenericTextBox + // + this.strGenericTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.strGenericTextBox.Location = new System.Drawing.Point(92, 154); + this.strGenericTextBox.MaxLength = 8; + this.strGenericTextBox.Name = "strGenericTextBox"; + this.strGenericTextBox.Size = new System.Drawing.Size(62, 20); + this.strGenericTextBox.TabIndex = 22; + this.strGenericTextBox.Text = ".placeho"; + this.strGenericTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // strGenericLabel + // + this.strGenericLabel.Location = new System.Drawing.Point(3, 156); + this.strGenericLabel.Name = "strGenericLabel"; + this.strGenericLabel.Size = new System.Drawing.Size(85, 23); + this.strGenericLabel.TabIndex = 21; + this.strGenericLabel.Text = "Generic string:"; + this.strGenericLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // popExplanationLabel + // + this.popExplanationLabel.AutoSize = true; + this.popExplanationLabel.Location = new System.Drawing.Point(7, 7); + this.popExplanationLabel.Name = "popExplanationLabel"; + this.popExplanationLabel.Size = new System.Drawing.Size(541, 13); + this.popExplanationLabel.TabIndex = 0; + this.popExplanationLabel.Text = "Select pseudo-op names for display. This does not affect source code generation. " + + "Blank entries get default value."; + // + // denseTextBox + // + this.denseTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.denseTextBox.Location = new System.Drawing.Point(546, 116); + this.denseTextBox.MaxLength = 8; + this.denseTextBox.Name = "denseTextBox"; + this.denseTextBox.Size = new System.Drawing.Size(62, 20); + this.denseTextBox.TabIndex = 20; + this.denseTextBox.Text = ".placeho"; + this.denseTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // denseLabel + // + this.denseLabel.Location = new System.Drawing.Point(460, 118); + this.denseLabel.Name = "denseLabel"; + this.denseLabel.Size = new System.Drawing.Size(82, 23); + this.denseLabel.TabIndex = 19; + this.denseLabel.Text = "Bulk data:"; + this.denseLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // fillTextBox + // + this.fillTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.fillTextBox.Location = new System.Drawing.Point(395, 116); + this.fillTextBox.MaxLength = 8; + this.fillTextBox.Name = "fillTextBox"; + this.fillTextBox.Size = new System.Drawing.Size(62, 20); + this.fillTextBox.TabIndex = 18; + this.fillTextBox.Text = ".placeho"; + this.fillTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // fillLabel + // + this.fillLabel.Location = new System.Drawing.Point(309, 118); + this.fillLabel.Name = "fillLabel"; + this.fillLabel.Size = new System.Drawing.Size(82, 23); + this.fillLabel.TabIndex = 17; + this.fillLabel.Text = "Fill:"; + this.fillLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // defineBigData2TextBox + // + this.defineBigData2TextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.defineBigData2TextBox.Location = new System.Drawing.Point(92, 116); + this.defineBigData2TextBox.MaxLength = 8; + this.defineBigData2TextBox.Name = "defineBigData2TextBox"; + this.defineBigData2TextBox.Size = new System.Drawing.Size(62, 20); + this.defineBigData2TextBox.TabIndex = 16; + this.defineBigData2TextBox.Text = ".placeho"; + this.defineBigData2TextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // defineBigData2Label + // + this.defineBigData2Label.Location = new System.Drawing.Point(3, 113); + this.defineBigData2Label.Name = "defineBigData2Label"; + this.defineBigData2Label.Size = new System.Drawing.Size(85, 32); + this.defineBigData2Label.TabIndex = 15; + this.defineBigData2Label.Text = "Big-endian data, two bytes:"; + this.defineBigData2Label.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // defineData4TextBox + // + this.defineData4TextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.defineData4TextBox.Location = new System.Drawing.Point(546, 78); + this.defineData4TextBox.MaxLength = 8; + this.defineData4TextBox.Name = "defineData4TextBox"; + this.defineData4TextBox.Size = new System.Drawing.Size(62, 20); + this.defineData4TextBox.TabIndex = 14; + this.defineData4TextBox.Text = ".placeho"; + this.defineData4TextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // defineData4Label + // + this.defineData4Label.Location = new System.Drawing.Point(463, 80); + this.defineData4Label.Name = "defineData4Label"; + this.defineData4Label.Size = new System.Drawing.Size(79, 23); + this.defineData4Label.TabIndex = 13; + this.defineData4Label.Text = "Four bytes:"; + this.defineData4Label.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // defineData3TextBox + // + this.defineData3TextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.defineData3TextBox.Location = new System.Drawing.Point(395, 78); + this.defineData3TextBox.MaxLength = 8; + this.defineData3TextBox.Name = "defineData3TextBox"; + this.defineData3TextBox.Size = new System.Drawing.Size(62, 20); + this.defineData3TextBox.TabIndex = 12; + this.defineData3TextBox.Text = ".placeho"; + this.defineData3TextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // defineData3Label + // + this.defineData3Label.Location = new System.Drawing.Point(309, 80); + this.defineData3Label.Name = "defineData3Label"; + this.defineData3Label.Size = new System.Drawing.Size(82, 23); + this.defineData3Label.TabIndex = 11; + this.defineData3Label.Text = "Three bytes:"; + this.defineData3Label.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // defineData2TextBox + // + this.defineData2TextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.defineData2TextBox.Location = new System.Drawing.Point(244, 78); + this.defineData2TextBox.MaxLength = 8; + this.defineData2TextBox.Name = "defineData2TextBox"; + this.defineData2TextBox.Size = new System.Drawing.Size(62, 20); + this.defineData2TextBox.TabIndex = 10; + this.defineData2TextBox.Text = ".placeho"; + this.defineData2TextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // defineData2Label + // + this.defineData2Label.Location = new System.Drawing.Point(160, 80); + this.defineData2Label.Name = "defineData2Label"; + this.defineData2Label.Size = new System.Drawing.Size(80, 23); + this.defineData2Label.TabIndex = 9; + this.defineData2Label.Text = "Two bytes:"; + this.defineData2Label.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // regWidthDirectiveTextBox + // + this.regWidthDirectiveTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.regWidthDirectiveTextBox.Location = new System.Drawing.Point(395, 40); + this.regWidthDirectiveTextBox.MaxLength = 8; + this.regWidthDirectiveTextBox.Name = "regWidthDirectiveTextBox"; + this.regWidthDirectiveTextBox.Size = new System.Drawing.Size(62, 20); + this.regWidthDirectiveTextBox.TabIndex = 6; + this.regWidthDirectiveTextBox.Text = ".placeho"; + this.regWidthDirectiveTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // regWidthDirectiveLabel + // + this.regWidthDirectiveLabel.Location = new System.Drawing.Point(312, 42); + this.regWidthDirectiveLabel.Name = "regWidthDirectiveLabel"; + this.regWidthDirectiveLabel.Size = new System.Drawing.Size(79, 23); + this.regWidthDirectiveLabel.TabIndex = 5; + this.regWidthDirectiveLabel.Text = "Reg width:"; + this.regWidthDirectiveLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // orgDirectiveTextBox + // + this.orgDirectiveTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.orgDirectiveTextBox.Location = new System.Drawing.Point(244, 40); + this.orgDirectiveTextBox.MaxLength = 8; + this.orgDirectiveTextBox.Name = "orgDirectiveTextBox"; + this.orgDirectiveTextBox.Size = new System.Drawing.Size(62, 20); + this.orgDirectiveTextBox.TabIndex = 4; + this.orgDirectiveTextBox.Text = ".placeho"; + this.orgDirectiveTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // orgDirectiveLabel + // + this.orgDirectiveLabel.Location = new System.Drawing.Point(160, 42); + this.orgDirectiveLabel.Name = "orgDirectiveLabel"; + this.orgDirectiveLabel.Size = new System.Drawing.Size(80, 23); + this.orgDirectiveLabel.TabIndex = 3; + this.orgDirectiveLabel.Text = "Origin:"; + this.orgDirectiveLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // defineData1TextBox + // + this.defineData1TextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.defineData1TextBox.Location = new System.Drawing.Point(92, 78); + this.defineData1TextBox.MaxLength = 8; + this.defineData1TextBox.Name = "defineData1TextBox"; + this.defineData1TextBox.Size = new System.Drawing.Size(62, 20); + this.defineData1TextBox.TabIndex = 8; + this.defineData1TextBox.Text = ".placeho"; + this.defineData1TextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // defineData1Label + // + this.defineData1Label.Location = new System.Drawing.Point(3, 74); + this.defineData1Label.Name = "defineData1Label"; + this.defineData1Label.Size = new System.Drawing.Size(85, 32); + this.defineData1Label.TabIndex = 7; + this.defineData1Label.Text = "Little-endian data, one byte:"; + this.defineData1Label.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // equDirectiveTextBox + // + this.equDirectiveTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.equDirectiveTextBox.Location = new System.Drawing.Point(92, 40); + this.equDirectiveTextBox.MaxLength = 8; + this.equDirectiveTextBox.Name = "equDirectiveTextBox"; + this.equDirectiveTextBox.Size = new System.Drawing.Size(62, 20); + this.equDirectiveTextBox.TabIndex = 2; + this.equDirectiveTextBox.Text = ".placeho"; + this.equDirectiveTextBox.TextChanged += new System.EventHandler(this.PseudoOpTextChanged); + // + // equDirectiveLabel + // + this.equDirectiveLabel.Location = new System.Drawing.Point(3, 42); + this.equDirectiveLabel.Name = "equDirectiveLabel"; + this.equDirectiveLabel.Size = new System.Drawing.Size(85, 23); + this.equDirectiveLabel.TabIndex = 1; + this.equDirectiveLabel.Text = "Equate:"; + this.equDirectiveLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // EditAppSettings + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(624, 441); + this.Controls.Add(this.settingsTabControl); + this.Controls.Add(this.applyButton); + this.Controls.Add(this.okButton); + this.Controls.Add(this.cancelButton); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditAppSettings"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Settings"; + this.Load += new System.EventHandler(this.EditAppSettings_Load); + this.settingsTabControl.ResumeLayout(false); + this.codeViewTabPage.ResumeLayout(false); + this.codeViewTabPage.PerformLayout(); + this.clipboardGroupBox.ResumeLayout(false); + this.clipboardGroupBox.PerformLayout(); + this.upperCaseGroupBox.ResumeLayout(false); + this.upperCaseGroupBox.PerformLayout(); + this.codeViewFontGroupBox.ResumeLayout(false); + this.codeViewFontGroupBox.PerformLayout(); + this.columnVisGroup.ResumeLayout(false); + this.asmConfigTabPage.ResumeLayout(false); + this.asmConfigTabPage.PerformLayout(); + this.displayFormatTabPage.ResumeLayout(false); + this.displayFormatTabPage.PerformLayout(); + this.quickDisplayFormatGroup.ResumeLayout(false); + this.operandWidthGroupBox.ResumeLayout(false); + this.operandWidthGroupBox.PerformLayout(); + this.pseudoOpTabPage.ResumeLayout(false); + this.pseudoOpTabPage.PerformLayout(); + this.quickPseudoSetGroup.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Button applyButton; + private System.Windows.Forms.TabControl settingsTabControl; + private System.Windows.Forms.TabPage codeViewTabPage; + private System.Windows.Forms.TabPage asmConfigTabPage; + private System.Windows.Forms.TabPage pseudoOpTabPage; + private System.Windows.Forms.GroupBox columnVisGroup; + private System.Windows.Forms.Button showCol0; + private System.Windows.Forms.Button showCol8; + private System.Windows.Forms.Button showCol1; + private System.Windows.Forms.Button showCol7; + private System.Windows.Forms.Button showCol2; + private System.Windows.Forms.Button showCol6; + private System.Windows.Forms.Button showCol3; + private System.Windows.Forms.Button showCol5; + private System.Windows.Forms.Button showCol4; + private System.Windows.Forms.TextBox defineData1TextBox; + private System.Windows.Forms.Label defineData1Label; + private System.Windows.Forms.TextBox equDirectiveTextBox; + private System.Windows.Forms.Label equDirectiveLabel; + private System.Windows.Forms.TextBox denseTextBox; + private System.Windows.Forms.Label denseLabel; + private System.Windows.Forms.TextBox fillTextBox; + private System.Windows.Forms.Label fillLabel; + private System.Windows.Forms.TextBox defineBigData2TextBox; + private System.Windows.Forms.Label defineBigData2Label; + private System.Windows.Forms.TextBox defineData4TextBox; + private System.Windows.Forms.Label defineData4Label; + private System.Windows.Forms.TextBox defineData3TextBox; + private System.Windows.Forms.Label defineData3Label; + private System.Windows.Forms.TextBox defineData2TextBox; + private System.Windows.Forms.Label defineData2Label; + private System.Windows.Forms.TextBox regWidthDirectiveTextBox; + private System.Windows.Forms.Label regWidthDirectiveLabel; + private System.Windows.Forms.TextBox orgDirectiveTextBox; + private System.Windows.Forms.Label orgDirectiveLabel; + private System.Windows.Forms.Label popExplanationLabel; + private System.Windows.Forms.TextBox strLen8HiTextBox; + private System.Windows.Forms.Label strLen8HiLabel; + private System.Windows.Forms.TextBox strLen8TextBox; + private System.Windows.Forms.Label strLen8Label; + private System.Windows.Forms.TextBox strGenericHiTextBox; + private System.Windows.Forms.Label strGenericHiLabel; + private System.Windows.Forms.TextBox strGenericTextBox; + private System.Windows.Forms.Label strGenericLabel; + private System.Windows.Forms.TextBox strNullTermHiTextBox; + private System.Windows.Forms.Label strNullTermHiLabel; + private System.Windows.Forms.TextBox strNullTermTextBox; + private System.Windows.Forms.Label strNullTermLabel; + private System.Windows.Forms.TextBox strDciHiTextBox; + private System.Windows.Forms.Label strDciHiLabel; + private System.Windows.Forms.TextBox strDciTextBox; + private System.Windows.Forms.Label strDciLabel; + private System.Windows.Forms.TextBox strLen16HiTextBox; + private System.Windows.Forms.Label strLen16HiLabel; + private System.Windows.Forms.TextBox strLen16TextBox; + private System.Windows.Forms.Label strLen16Label; + private System.Windows.Forms.TextBox strReverseHiTextBox; + private System.Windows.Forms.Label strReverseHiLabel; + private System.Windows.Forms.TextBox strReverseTextBox; + private System.Windows.Forms.Label strReverseLabel; + private System.Windows.Forms.Label asmExplanationLabel; + private System.Windows.Forms.TextBox cc65PathTextBox; + private System.Windows.Forms.TextBox merlin32PathTextBox; + private System.Windows.Forms.Label asmMerln32Label; + private System.Windows.Forms.Label asmCc65Label; + private System.Windows.Forms.Button browseMerlin32Button; + private System.Windows.Forms.Button browseCc65Button; + private System.Windows.Forms.GroupBox codeViewFontGroupBox; + private System.Windows.Forms.Label currentFontLabel; + private System.Windows.Forms.Button selectFontButton; + private System.Windows.Forms.Label currentFontDisplayLabel; + private System.Windows.Forms.GroupBox quickPseudoSetGroup; + private System.Windows.Forms.Button quickPseudoMerlin32; + private System.Windows.Forms.Button quickPseudoCc65Button; + private System.Windows.Forms.Button quickPseudoDefaultButton; + private System.Windows.Forms.GroupBox upperCaseGroupBox; + private System.Windows.Forms.CheckBox upperOpcodeCheckBox; + private System.Windows.Forms.CheckBox upperHexCheckBox; + private System.Windows.Forms.Button upperAllUpperButton; + private System.Windows.Forms.Button upperAllLowerButton; + private System.Windows.Forms.CheckBox upperXYCheckBox; + private System.Windows.Forms.CheckBox upperSCheckBox; + private System.Windows.Forms.CheckBox upperACheckBox; + private System.Windows.Forms.CheckBox upperPseudoOpCheckBox; + private System.Windows.Forms.Button clearMerlin32Button; + private System.Windows.Forms.Button clearCc65Button; + private System.Windows.Forms.CheckBox disableLabelLocalizationCheckBox; + private System.Windows.Forms.CheckBox enableDebugCheckBox; + private System.Windows.Forms.CheckBox showAsmIdentCheckBox; + private System.Windows.Forms.TabPage displayFormatTabPage; + private System.Windows.Forms.CheckBox useMerlinExpressions; + private System.Windows.Forms.GroupBox operandWidthGroupBox; + private System.Windows.Forms.TextBox disambPrefix24TextBox; + private System.Windows.Forms.TextBox disambPrefix16TextBox; + private System.Windows.Forms.Label disambPrefix24Label; + private System.Windows.Forms.Label disambPrefix16Label; + private System.Windows.Forms.Label disambOperandPrefixLabel; + private System.Windows.Forms.Label disambSuffix24Label; + private System.Windows.Forms.Label disambSuffix16Label; + private System.Windows.Forms.Label disambOpcodeSuffixLabel; + private System.Windows.Forms.TextBox disambSuffix24TextBox; + private System.Windows.Forms.TextBox disambSuffix16TextBox; + private System.Windows.Forms.GroupBox quickDisplayFormatGroup; + private System.Windows.Forms.Button quickFmtMerlin32Button; + private System.Windows.Forms.Button quickFmtCc65Button; + private System.Windows.Forms.Button quickFmtDefaultButton; + private System.Windows.Forms.Label configAsmGenLabel; + private System.Windows.Forms.CheckBox longLabelNewLineCheckBox; + private System.Windows.Forms.Label fmtExplanationLabel; + private System.Windows.Forms.CheckBox showCycleCountsCheckBox; + private System.Windows.Forms.GroupBox clipboardGroupBox; + private System.Windows.Forms.ComboBox clipboardFormatComboBox; + private System.Windows.Forms.Label clipboardFormatLabel; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/EditAppSettings.cs b/SourceGen/AppForms/EditAppSettings.cs new file mode 100644 index 0000000..baf55a8 --- /dev/null +++ b/SourceGen/AppForms/EditAppSettings.cs @@ -0,0 +1,617 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Drawing; +using System.Reflection; +using System.Text; +using System.Windows.Forms; + +namespace SourceGen.AppForms { + public partial class EditAppSettings : Form { + /// + /// Tab page enumeration. Numbers must match page indices in designer. + /// + public enum Tab { + Unknown = -1, + CodeList = 0, + Assembler = 1, + AsmFormat = 2, + PseudoOp = 3 + } + + /// + /// ProjectView reference. When the user hits Apply, the object's ApplyAppSettings + /// method will be invoked. + /// + private ProjectView mProjectView; + + /// + /// Copy of settings that we make changes to. On "Apply" or "OK", this is pushed + /// into the global settings object, and applied to the ProjectView. + /// + private AppSettings mSettings; + + /// + /// Dirty flag, set when anything in mSettings changes. Don't modify this directly. Use + /// the SetDirty() call so that the Apply button's enabled status gets updated. + /// + private bool mDirty; + + /// + /// Tab to show when dialog is first opened. + /// + private Tab mInitialTab; + + // Map buttons to column show/hide buttons. + private const int NUM_COLUMNS = ProjectView.CodeListColumnWidths.NUM_COLUMNS; + private string[] mColumnFormats = new string[NUM_COLUMNS]; + private Button[] mColButtons; + + // Map pseudo-op text entry fields to PseudoOpName properties. + private struct TextBoxPropertyMap { + public TextBox TextBox { get; private set; } + public PropertyInfo PropInfo { get; private set; } + + public TextBoxPropertyMap(TextBox textBox, string propName) { + TextBox = textBox; + PropInfo = typeof(PseudoOp.PseudoOpNames).GetProperty(propName); + } + } + private TextBoxPropertyMap[] mPseudoNameMap; + + + public EditAppSettings(ProjectView projectView, Tab initialTab) { + InitializeComponent(); + + mProjectView = projectView; + mInitialTab = initialTab; + + // Make a work copy, so we can discard changes if the user cancels out of the dialog. + projectView.SaveCodeListColumnWidths(); + mSettings = AppSettings.Global.GetCopy(); + + // Put buttons in an array. + mColButtons = new Button[] { + showCol0, showCol1, showCol2, showCol3, showCol4, + showCol5, showCol6, showCol7, showCol8 }; + Debug.Assert(NUM_COLUMNS == 9); + + // Extract formats from button labels. + for (int i = 0; i < NUM_COLUMNS; i++) { + mColButtons[i].Click += ColumnVisibilityButtonClick; + mColumnFormats[i] = mColButtons[i].Text; + } + + // Map text boxes to PseudoOpName fields. + mPseudoNameMap = new TextBoxPropertyMap[] { + new TextBoxPropertyMap(equDirectiveTextBox, "EquDirective"), + new TextBoxPropertyMap(orgDirectiveTextBox, "OrgDirective"), + new TextBoxPropertyMap(regWidthDirectiveTextBox, "RegWidthDirective"), + new TextBoxPropertyMap(defineData1TextBox, "DefineData1"), + new TextBoxPropertyMap(defineData2TextBox, "DefineData2"), + new TextBoxPropertyMap(defineData3TextBox, "DefineData3"), + new TextBoxPropertyMap(defineData4TextBox, "DefineData4"), + new TextBoxPropertyMap(defineBigData2TextBox, "DefineBigData2"), + new TextBoxPropertyMap(fillTextBox, "Fill"), + new TextBoxPropertyMap(denseTextBox, "Dense"), + new TextBoxPropertyMap(strGenericTextBox, "StrGeneric"), + new TextBoxPropertyMap(strGenericHiTextBox, "StrGenericHi"), + new TextBoxPropertyMap(strReverseTextBox, "StrReverse"), + new TextBoxPropertyMap(strReverseHiTextBox, "StrReverseHi"), + new TextBoxPropertyMap(strLen8TextBox, "StrLen8"), + new TextBoxPropertyMap(strLen8HiTextBox, "StrLen8Hi"), + new TextBoxPropertyMap(strLen16TextBox, "StrLen16"), + new TextBoxPropertyMap(strLen16HiTextBox, "StrLen16Hi"), + new TextBoxPropertyMap(strNullTermTextBox, "StrNullTerm"), + new TextBoxPropertyMap(strNullTermHiTextBox, "StrNullTermHi"), + new TextBoxPropertyMap(strDciTextBox, "StrDci"), + new TextBoxPropertyMap(strDciHiTextBox, "StrDciHi"), + }; + } + + private void EditAppSettings_Load(object sender, EventArgs e) { + // Column widths. We called SaveCodeListColumnWidths() earlier, so this + // should always be a valid serialized string. + string widthStr = mSettings.GetString(AppSettings.CDLV_COL_WIDTHS, null); + Debug.Assert(!string.IsNullOrEmpty(widthStr)); + ProjectView.CodeListColumnWidths widths = + ProjectView.CodeListColumnWidths.Deserialize(widthStr); + Debug.Assert(widths != null); + for (int i = 0; i < NUM_COLUMNS; i++) { + SetShowHideButton(i, widths.Width[i]); + } + + // Display localized font string. + FontConverter cvt = new FontConverter(); + currentFontDisplayLabel.Text = cvt.ConvertToString(mProjectView.CodeListViewFont); + + // Upper-case formatting. + upperHexCheckBox.Checked = mSettings.GetBool(AppSettings.FMT_UPPER_HEX_DIGITS, false); + upperOpcodeCheckBox.Checked = mSettings.GetBool( + AppSettings.FMT_UPPER_OP_MNEMONIC, false); + upperPseudoOpCheckBox.Checked = mSettings.GetBool( + AppSettings.FMT_UPPER_PSEUDO_OP_MNEMONIC, false); + upperACheckBox.Checked = mSettings.GetBool(AppSettings.FMT_UPPER_OPERAND_A, false); + upperSCheckBox.Checked = mSettings.GetBool(AppSettings.FMT_UPPER_OPERAND_S, false); + upperXYCheckBox.Checked = mSettings.GetBool(AppSettings.FMT_UPPER_OPERAND_XY, false); + + int clipIndex = mSettings.GetInt(AppSettings.CLIP_LINE_FORMAT, 0); + if (clipIndex >= 0 && clipIndex < clipboardFormatComboBox.Items.Count) { + clipboardFormatComboBox.SelectedIndex = clipIndex; + } + + enableDebugCheckBox.Checked = mSettings.GetBool(AppSettings.DEBUG_MENU_ENABLED, false); + + // Assemblers. + cc65PathTextBox.Text = + mSettings.GetString(AppSettings.ASM_CC65_EXECUTABLE, string.Empty); + merlin32PathTextBox.Text = + mSettings.GetString(AppSettings.ASM_MERLIN32_EXECUTABLE, string.Empty); + showAsmIdentCheckBox.Checked = + mSettings.GetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, false); + disableLabelLocalizationCheckBox.Checked = + mSettings.GetBool(AppSettings.SRCGEN_DISABLE_LABEL_LOCALIZATION, false); + longLabelNewLineCheckBox.Checked = + mSettings.GetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, false); + showCycleCountsCheckBox.Checked = + mSettings.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, false); + + // Pseudo ops. + string opStrCereal = mSettings.GetString(AppSettings.FMT_PSEUDO_OP_NAMES, null); + if (!string.IsNullOrEmpty(opStrCereal)) { + PseudoOp.PseudoOpNames opNames = PseudoOp.PseudoOpNames.Deserialize(opStrCereal); + ImportPseudoOpNames(opNames); + } else { + // no data available, populate with blanks + //PseudoOp.PseudoOpNames opNames = PseudoOp.sDefaultPseudoOpNames; + ImportPseudoOpNames(new PseudoOp.PseudoOpNames()); + } + + PopulateWidthDisamSettings(); + + string exprMode = mSettings.GetString(AppSettings.FMT_EXPRESSION_MODE, string.Empty); + useMerlinExpressions.Checked = + (Asm65.Formatter.FormatConfig.ParseExpressionMode(exprMode) == + Asm65.Formatter.FormatConfig.ExpressionMode.Merlin); + + if (mInitialTab != Tab.Unknown) { + settingsTabControl.SelectTab((int)mInitialTab); + } + + mDirty = false; + UpdateControls(); + } + + /// + /// Updates controls. + /// + private void UpdateControls() { + applyButton.Enabled = mDirty; + + clearMerlin32Button.Enabled = !string.IsNullOrEmpty(merlin32PathTextBox.Text); + clearCc65Button.Enabled = !string.IsNullOrEmpty(cc65PathTextBox.Text); + } + + /// + /// Sets the dirty flag and updates the controls. + /// + /// New value for dirty flag. + private void SetDirty(bool dirty) { + mDirty = dirty; + UpdateControls(); + } + + private void okButton_Click(object sender, EventArgs e) { + ApplySettings(); + } + + private void applyButton_Click(object sender, EventArgs e) { + ApplySettings(); + } + + private void ApplySettings() { + PseudoOp.PseudoOpNames opNames = ExportPseudoOpNames(); + string pseudoCereal = opNames.Serialize(); + mSettings.SetString(AppSettings.FMT_PSEUDO_OP_NAMES, pseudoCereal); + + mProjectView.SetAppSettings(mSettings); + AsmGen.AssemblerVersionCache.QueryVersions(); + SetDirty(false); + } + + + #region Code View + + /// + /// Updates the text on a show/hide column button. + /// + /// Column index. + /// New width. + private void SetShowHideButton(int index, int width) { + Button button = mColButtons[index]; + string fmt = mColumnFormats[index]; + string show = Properties.Resources.SHOW_COL; + string hide = Properties.Resources.HIDE_COL; + button.Text = string.Format(fmt, (width == 0) ? show : hide); + } + + /// + /// Handler for all show/hide column buttons. + /// + /// Identifies the button that was clicked. + /// Stuff. + private void ColumnVisibilityButtonClick(object sender, EventArgs e) { + int index = -1; + for (int i = 0; i < mColButtons.Length; i++) { + if (sender == mColButtons[i]) { + index = i; + break; + } + } + Debug.Assert(index != -1); + + string widthStr = mSettings.GetString(AppSettings.CDLV_COL_WIDTHS, null); + Debug.Assert(!string.IsNullOrEmpty(widthStr)); + ProjectView.CodeListColumnWidths widths = + ProjectView.CodeListColumnWidths.Deserialize(widthStr); + if (widths.Width[index] == 0) { + // Expand to default width. The default width changes when the font + // changes, so it's best to just reacquire the default width set as needed. + ProjectView.CodeListColumnWidths defaultWidths = + mProjectView.GetDefaultCodeListColumnWidths(); + widths.Width[index] = defaultWidths.Width[index]; + } else { + widths.Width[index] = 0; + } + widthStr = widths.Serialize(); + mSettings.SetString(AppSettings.CDLV_COL_WIDTHS, widthStr); + SetShowHideButton(index, widths.Width[index]); + + SetDirty(true); + } + + private void selectFontButton_Click(object sender, EventArgs e) { + FontDialog dlg = new FontDialog(); + dlg.Font = mProjectView.CodeListViewFont; + dlg.ShowEffects = false; + Debug.WriteLine("Showing font dialog..."); + if (dlg.ShowDialog() != DialogResult.Cancel) { + FontConverter cvt = new FontConverter(); + // Store invariant string, display localized string. + mSettings.SetString(AppSettings.CDLV_FONT, cvt.ConvertToInvariantString(dlg.Font)); + currentFontDisplayLabel.Text = cvt.ConvertToString(dlg.Font); + SetDirty(true); + } + Debug.WriteLine("Font dialog done..."); + dlg.Dispose(); + } + + private void upperHexCheckBox_CheckedChanged(object sender, EventArgs e) { + mSettings.SetBool(AppSettings.FMT_UPPER_HEX_DIGITS, upperHexCheckBox.Checked); + SetDirty(true); + } + private void upperOpcodeCheckBox_CheckedChanged(object sender, EventArgs e) { + mSettings.SetBool(AppSettings.FMT_UPPER_OP_MNEMONIC, upperOpcodeCheckBox.Checked); + SetDirty(true); + } + private void upperPseudoOpCheckBox_CheckedChanged(object sender, EventArgs e) { + mSettings.SetBool(AppSettings.FMT_UPPER_PSEUDO_OP_MNEMONIC, + upperPseudoOpCheckBox.Checked); + SetDirty(true); + } + private void upperACheckBox_CheckedChanged(object sender, EventArgs e) { + mSettings.SetBool(AppSettings.FMT_UPPER_OPERAND_A, upperACheckBox.Checked); + SetDirty(true); + } + private void upperSCheckBox_CheckedChanged(object sender, EventArgs e) { + mSettings.SetBool(AppSettings.FMT_UPPER_OPERAND_S, upperSCheckBox.Checked); + SetDirty(true); + } + private void upperXYCheckBox_CheckedChanged(object sender, EventArgs e) { + mSettings.SetBool(AppSettings.FMT_UPPER_OPERAND_XY, upperXYCheckBox.Checked); + SetDirty(true); + } + private void upperAllLowerButton_Click(object sender, EventArgs e) { + upperHexCheckBox.Checked = + upperOpcodeCheckBox.Checked = + upperPseudoOpCheckBox.Checked = + upperACheckBox.Checked = + upperSCheckBox.Checked = + upperXYCheckBox.Checked = false; + } + private void upperAllUpperButton_Click(object sender, EventArgs e) { + upperHexCheckBox.Checked = + upperOpcodeCheckBox.Checked = + upperPseudoOpCheckBox.Checked = + upperACheckBox.Checked = + upperSCheckBox.Checked = + upperXYCheckBox.Checked = true; + } + + private void clipboardFormatComboBox_SelectedIndexChanged(object sender, EventArgs e) { + mSettings.SetInt(AppSettings.CLIP_LINE_FORMAT, clipboardFormatComboBox.SelectedIndex); + SetDirty(true); + } + + private void enableDebugCheckBox_CheckedChanged(object sender, EventArgs e) { + mSettings.SetBool(AppSettings.DEBUG_MENU_ENABLED, enableDebugCheckBox.Checked); + SetDirty(true); + } + + #endregion Code View + + + #region Asm Config + + private void browseCc65Button_Click(object sender, EventArgs e) { + string pathName = BrowseForExecutable("cc65 CL", "cl65.exe"); + if (pathName != null) { + cc65PathTextBox.Text = pathName; + mSettings.SetString(AppSettings.ASM_CC65_EXECUTABLE, pathName); + } + } + + private void cc65PathTextBox_TextChanged(object sender, EventArgs e) { + mSettings.SetString(AppSettings.ASM_CC65_EXECUTABLE, cc65PathTextBox.Text); + SetDirty(true); + } + + private void clearCc65Button_Click(object sender, EventArgs e) { + cc65PathTextBox.Text = string.Empty; + mSettings.SetString(AppSettings.ASM_CC65_EXECUTABLE, null); + SetDirty(true); + } + + private void browseMerlin32Button_Click(object sender, EventArgs e) { + string pathName = BrowseForExecutable("Merlin Assembler", "Merlin32.exe"); + if (pathName != null) { + merlin32PathTextBox.Text = pathName; + mSettings.SetString(AppSettings.ASM_MERLIN32_EXECUTABLE, pathName); + } + } + + private void clearMerlin32Button_Click(object sender, EventArgs e) { + merlin32PathTextBox.Text = string.Empty; + mSettings.SetString(AppSettings.ASM_MERLIN32_EXECUTABLE, null); + SetDirty(true); + } + + private void merlin32PathTextBox_TextChanged(object sender, EventArgs e) { + mSettings.SetString(AppSettings.ASM_MERLIN32_EXECUTABLE, merlin32PathTextBox.Text); + SetDirty(true); + } + + /// + /// Creates a file dialog to search for a specific executable. + /// + /// Human-readable filter string for UI. + /// Filename of executable. + /// Path of executable, or null if dialog was canceled. + private string BrowseForExecutable(string prefix, string name) { + string pathName = null; + + OpenFileDialog dlg = new OpenFileDialog(); + dlg.FileName = name; + dlg.Filter = prefix + "|" + name; + dlg.RestoreDirectory = true; + if (dlg.ShowDialog() != DialogResult.Cancel) { + pathName = dlg.FileName; + SetDirty(true); + } + dlg.Dispose(); + + return pathName; + } + + private void showAsmIdentCheckBox_CheckedChanged(object sender, EventArgs e) { + mSettings.SetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, showAsmIdentCheckBox.Checked); + SetDirty(true); + } + + private void disableLabelLocalizationCheckBox_CheckedChanged(object sender, EventArgs e) { + mSettings.SetBool(AppSettings.SRCGEN_DISABLE_LABEL_LOCALIZATION, + disableLabelLocalizationCheckBox.Checked); + SetDirty(true); + } + + private void longLabelNewLineCheckBox_CheckedChanged(object sender, EventArgs e) { + mSettings.SetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, + longLabelNewLineCheckBox.Checked); + SetDirty(true); + } + + private void showCycleCountsCheckBox_CheckedChanged(object sender, EventArgs e) { + mSettings.SetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, + showCycleCountsCheckBox.Checked); + SetDirty(true); + } + + #endregion Asm Config + + + #region Display Format + + /// + /// Populates the width disambiguation text boxes. + /// + private void PopulateWidthDisamSettings() { + // Operand width disambiguation. This is a little tricky -- we have to query all + // settings then set all controls, or the field-updated callback may interfere + // with us by changing AppSettings. + string opcSuffixAbs = mSettings.GetString(AppSettings.FMT_OPCODE_SUFFIX_ABS, + string.Empty); + string opcSuffixLong = mSettings.GetString(AppSettings.FMT_OPCODE_SUFFIX_LONG, + string.Empty); + string opPrefixAbs = mSettings.GetString(AppSettings.FMT_OPERAND_PREFIX_ABS, + string.Empty); + string opPrefixLong = mSettings.GetString(AppSettings.FMT_OPERAND_PREFIX_LONG, + string.Empty); + + disambSuffix16TextBox.Text = opcSuffixAbs; + disambSuffix24TextBox.Text = opcSuffixLong; + disambPrefix16TextBox.Text = opPrefixAbs; + disambPrefix24TextBox.Text = opPrefixLong; + } + + /// + /// Sets all of the width disambiguation settings. Used for the quick-set buttons. + /// + private void SetWidthDisamSettings(string opcodeSuffixAbs, string opcodeSuffixLong, + string operandPrefixAbs, string operandPrefixLong) { + mSettings.SetString(AppSettings.FMT_OPCODE_SUFFIX_ABS, opcodeSuffixAbs); + mSettings.SetString(AppSettings.FMT_OPCODE_SUFFIX_LONG, opcodeSuffixLong); + mSettings.SetString(AppSettings.FMT_OPERAND_PREFIX_ABS, operandPrefixAbs); + mSettings.SetString(AppSettings.FMT_OPERAND_PREFIX_LONG, operandPrefixLong); + PopulateWidthDisamSettings(); + } + + // Called when text is typed. + private void WidthDisamControlChanged(object sender, EventArgs e) { + ExportWidthDisamSettings(); + } + + /// + /// Exports the current state of the width controls to the settings object. + /// + private void ExportWidthDisamSettings() { + mSettings.SetString(AppSettings.FMT_OPCODE_SUFFIX_ABS, disambSuffix16TextBox.Text); + mSettings.SetString(AppSettings.FMT_OPCODE_SUFFIX_LONG, disambSuffix24TextBox.Text); + mSettings.SetString(AppSettings.FMT_OPERAND_PREFIX_ABS, disambPrefix16TextBox.Text); + mSettings.SetString(AppSettings.FMT_OPERAND_PREFIX_LONG, disambPrefix24TextBox.Text); + SetDirty(true); + + //Debug.WriteLine("disam: '" + + // mSettings.GetString(AppSettings.FMT_OPCODE_SUFFIX_ABS, string.Empty) + "' '" + + // mSettings.GetString(AppSettings.FMT_OPCODE_SUFFIX_LONG, string.Empty) + "' '" + + // mSettings.GetString(AppSettings.FMT_OPERAND_PREFIX_ABS, string.Empty) + "' '" + + // mSettings.GetString(AppSettings.FMT_OPERAND_PREFIX_LONG, string.Empty) + "'"); + } + + private void shiftAfterAdjustCheckBox_CheckedChanged(object sender, EventArgs e) { + string mode = useMerlinExpressions.Checked ? + Asm65.Formatter.FormatConfig.ExpressionMode.Merlin.ToString() : + Asm65.Formatter.FormatConfig.ExpressionMode.Simple.ToString(); + mSettings.SetString(AppSettings.FMT_EXPRESSION_MODE, mode); + SetDirty(true); + } + + private void quickFmtDefaultButton_Click(object sender, EventArgs e) { + SetWidthDisamSettings(null, "l", "a:", "f:"); + useMerlinExpressions.Checked = false; + // dirty flag set by change callbacks + } + + private void quickFmtCc65Button_Click(object sender, EventArgs e) { + SetWidthDisamSettings(null, null, "a:", "f:"); + useMerlinExpressions.Checked = false; + // dirty flag set by change callbacks + } + + private void quickFmtMerlin32Button_Click(object sender, EventArgs e) { + SetWidthDisamSettings(":", "l", null, null); + useMerlinExpressions.Checked = true; + // dirty flag set by change callbacks + } + + #endregion Display Format + + + #region Pseudo-Op + + /// + /// Imports values from PseudoOpNames struct into text fields. + /// + private void ImportPseudoOpNames(PseudoOp.PseudoOpNames opNames) { + for (int i = 0; i < mPseudoNameMap.Length; i++) { + string str = (string)mPseudoNameMap[i].PropInfo.GetValue(opNames); + mPseudoNameMap[i].TextBox.Text = (str == null) ? string.Empty : str; + } + } + + /// + /// Exports values from text fields to a PseudoOpNames object. + /// + private PseudoOp.PseudoOpNames ExportPseudoOpNames() { + PseudoOp.PseudoOpNames opNames = new PseudoOp.PseudoOpNames(); + for (int i = 0; i < mPseudoNameMap.Length; i++) { + // NOTE: PseudoOpNames must be a class (not a struct) or this will fail. + // SetValue() would be invoked on a boxed copy that is discarded afterward. + mPseudoNameMap[i].PropInfo.SetValue(opNames, mPseudoNameMap[i].TextBox.Text); + } + return opNames; + } + + // Invoked when text is changed in any pseudo-op text box. + private void PseudoOpTextChanged(object sender, EventArgs e) { + // Just set the dirty flag. The (somewhat expensive) export will happen + // on Apply/OK. + SetDirty(true); + } + + private void quickPseudoDefaultButton_Click(object sender, EventArgs e) { + ImportPseudoOpNames(new PseudoOp.PseudoOpNames()); + } + + private void quickPseudoCc65Button_Click(object sender, EventArgs e) { + ImportPseudoOpNames(new PseudoOp.PseudoOpNames() { + EquDirective = "=", + OrgDirective = ".org", + DefineData1 = ".byte", + DefineData2 = ".word", + DefineData3 = ".faraddr", + DefineData4 = ".dword", + DefineBigData2 = ".dbyt", + Fill = ".res", + StrGeneric = ".byte", + StrNullTerm = ".asciiz", + }); + } + + private void quickPseudoMerlin32_Click(object sender, EventArgs e) { + // Note this doesn't quite match up with the Merlin generator, which uses + // the same pseudo-op for low/high ASCII but different string delimiters. We + // don't change the delimiters for the display list, so we want to tweak the + // opcode slightly. + //char hiAscii = '\u21e1'; + char hiAscii = '\u2191'; + ImportPseudoOpNames(new PseudoOp.PseudoOpNames() { + EquDirective = "equ", + OrgDirective = "org", + DefineData1 = "dfb", + DefineData2 = "dw", + DefineData3 = "adr", + DefineData4 = "adrl", + DefineBigData2 = "ddb", + Fill = "ds", + Dense = "hex", + StrGeneric = "asc", + StrGenericHi = "asc" + hiAscii, + StrReverse = "rev", + StrReverseHi = "rev" + hiAscii, + StrLen8 = "str", + StrLen8Hi = "str" + hiAscii, + StrLen16 = "strl", + StrLen16Hi = "strl" + hiAscii, + StrDci = "dci", + StrDciHi = "dci" + hiAscii, + }); + } + + #endregion Pseudo-Op + } +} diff --git a/SourceGen/AppForms/EditAppSettings.resx b/SourceGen/AppForms/EditAppSettings.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/EditAppSettings.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/EditComment.Designer.cs b/SourceGen/AppForms/EditComment.Designer.cs new file mode 100644 index 0000000..3ac1317 --- /dev/null +++ b/SourceGen/AppForms/EditComment.Designer.cs @@ -0,0 +1,153 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class EditComment { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.cancelButton = new System.Windows.Forms.Button(); + this.okButton = new System.Windows.Forms.Button(); + this.instructionLabel = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.asciiOnlyLabel = new System.Windows.Forms.Label(); + this.maxLengthLabel = new System.Windows.Forms.Label(); + this.numCharsLabel = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(426, 105); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 6; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(345, 105); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 5; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // instructionLabel + // + this.instructionLabel.AutoSize = true; + this.instructionLabel.Location = new System.Drawing.Point(13, 13); + this.instructionLabel.Name = "instructionLabel"; + this.instructionLabel.Size = new System.Drawing.Size(81, 13); + this.instructionLabel.TabIndex = 0; + this.instructionLabel.Text = "Enter comment:"; + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(13, 30); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(488, 20); + this.textBox1.TabIndex = 1; + this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged); + // + // asciiOnlyLabel + // + this.asciiOnlyLabel.AutoSize = true; + this.asciiOnlyLabel.Location = new System.Drawing.Point(13, 57); + this.asciiOnlyLabel.Name = "asciiOnlyLabel"; + this.asciiOnlyLabel.Size = new System.Drawing.Size(145, 13); + this.asciiOnlyLabel.TabIndex = 2; + this.asciiOnlyLabel.Text = "• ASCII-only is recommended"; + // + // maxLengthLabel + // + this.maxLengthLabel.AutoSize = true; + this.maxLengthLabel.Location = new System.Drawing.Point(13, 74); + this.maxLengthLabel.Name = "maxLengthLabel"; + this.maxLengthLabel.Size = new System.Drawing.Size(281, 13); + this.maxLengthLabel.TabIndex = 3; + this.maxLengthLabel.Text = "• Limit to 52 or fewer characters for nice 80-column output"; + // + // numCharsLabel + // + this.numCharsLabel.Location = new System.Drawing.Point(386, 53); + this.numCharsLabel.Name = "numCharsLabel"; + this.numCharsLabel.Size = new System.Drawing.Size(115, 23); + this.numCharsLabel.TabIndex = 4; + this.numCharsLabel.Text = "{0} characters"; + this.numCharsLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // EditComment + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(513, 140); + this.Controls.Add(this.numCharsLabel); + this.Controls.Add(this.maxLengthLabel); + this.Controls.Add(this.asciiOnlyLabel); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.instructionLabel); + this.Controls.Add(this.okButton); + this.Controls.Add(this.cancelButton); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditComment"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Comment"; + this.Load += new System.EventHandler(this.EditComment_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Label instructionLabel; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Label asciiOnlyLabel; + private System.Windows.Forms.Label maxLengthLabel; + private System.Windows.Forms.Label numCharsLabel; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/EditComment.cs b/SourceGen/AppForms/EditComment.cs new file mode 100644 index 0000000..dc50bf2 --- /dev/null +++ b/SourceGen/AppForms/EditComment.cs @@ -0,0 +1,72 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Drawing; +using System.Windows.Forms; + +namespace SourceGen.AppForms { + public partial class EditComment : Form { + /// + /// Comment string being edited. + /// + public string Comment { get; set; } + + private string mNumCharsFormat; + + private Color mDefaultLabelColor; + + private const int RECOMMENDED_MAX_LENGTH = 52; + + + public EditComment() { + InitializeComponent(); + } + + private void EditComment_Load(object sender, EventArgs e) { + mDefaultLabelColor = asciiOnlyLabel.ForeColor; + + // Extract the format string from the label. + mNumCharsFormat = numCharsLabel.Text; + + textBox1.Text = Comment; + UpdateLengthLabel(); + } + + private void UpdateLengthLabel() { + numCharsLabel.Text = string.Format(mNumCharsFormat, textBox1.Text.Length); + } + + private void textBox1_TextChanged(object sender, EventArgs e) { + UpdateLengthLabel(); + + if (!CommonUtil.TextUtil.IsPrintableAscii(textBox1.Text)) { + asciiOnlyLabel.ForeColor = Color.Red; + } else { + asciiOnlyLabel.ForeColor = mDefaultLabelColor; + } + if (textBox1.Text.Length > RECOMMENDED_MAX_LENGTH) { + maxLengthLabel.ForeColor = Color.Red; + } else { + maxLengthLabel.ForeColor = mDefaultLabelColor; + } + } + + private void okButton_Click(object sender, EventArgs e) { + Comment = textBox1.Text; + } + } +} diff --git a/SourceGen/AppForms/EditComment.resx b/SourceGen/AppForms/EditComment.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/EditComment.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/EditData.Designer.cs b/SourceGen/AppForms/EditData.Designer.cs new file mode 100644 index 0000000..760831d --- /dev/null +++ b/SourceGen/AppForms/EditData.Designer.cs @@ -0,0 +1,559 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class EditData { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.cancelButton = new System.Windows.Forms.Button(); + this.okButton = new System.Windows.Forms.Button(); + this.selectFormatLabel = new System.Windows.Forms.Label(); + this.rawDataSectionLabel = new System.Windows.Forms.Label(); + this.radioSingleBytes = new System.Windows.Forms.RadioButton(); + this.radio16BitBig = new System.Windows.Forms.RadioButton(); + this.radio16BitLittle = new System.Windows.Forms.RadioButton(); + this.radio24BitLittle = new System.Windows.Forms.RadioButton(); + this.radio32BitLittle = new System.Windows.Forms.RadioButton(); + this.radioSimpleDataBinary = new System.Windows.Forms.RadioButton(); + this.radioSimpleDataDecimal = new System.Windows.Forms.RadioButton(); + this.radioSimpleDataHex = new System.Windows.Forms.RadioButton(); + this.radioDenseHex = new System.Windows.Forms.RadioButton(); + this.radioFill = new System.Windows.Forms.RadioButton(); + this.horizontalLine1 = new System.Windows.Forms.Label(); + this.horizontalLine2 = new System.Windows.Forms.Label(); + this.stringSectionLabel = new System.Windows.Forms.Label(); + this.horizontalLine3 = new System.Windows.Forms.Label(); + this.radioStringMixed = new System.Windows.Forms.RadioButton(); + this.radioStringMixedReverse = new System.Windows.Forms.RadioButton(); + this.radioStringNullTerm = new System.Windows.Forms.RadioButton(); + this.radioStringLen8 = new System.Windows.Forms.RadioButton(); + this.radioStringLen16 = new System.Windows.Forms.RadioButton(); + this.radioStringDci = new System.Windows.Forms.RadioButton(); + this.symbolEntryTextBox = new System.Windows.Forms.TextBox(); + this.symbolPartPanel = new System.Windows.Forms.Panel(); + this.radioSymbolPartBank = new System.Windows.Forms.RadioButton(); + this.radioSymbolPartHigh = new System.Windows.Forms.RadioButton(); + this.radioSymbolPartLow = new System.Windows.Forms.RadioButton(); + this.radioSimpleDataAscii = new System.Windows.Forms.RadioButton(); + this.radioDefaultFormat = new System.Windows.Forms.RadioButton(); + this.simpleDisplayAsGroupBox = new System.Windows.Forms.GroupBox(); + this.radioSimpleDataAddress = new System.Windows.Forms.RadioButton(); + this.radioSimpleDataSymbolic = new System.Windows.Forms.RadioButton(); + this.label1 = new System.Windows.Forms.Label(); + this.symbolPartPanel.SuspendLayout(); + this.simpleDisplayAsGroupBox.SuspendLayout(); + this.SuspendLayout(); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(438, 461); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 23; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(357, 461); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 22; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // selectFormatLabel + // + this.selectFormatLabel.AutoSize = true; + this.selectFormatLabel.Location = new System.Drawing.Point(12, 9); + this.selectFormatLabel.Name = "selectFormatLabel"; + this.selectFormatLabel.Size = new System.Drawing.Size(253, 13); + this.selectFormatLabel.TabIndex = 0; + this.selectFormatLabel.Text = "Select data format ({0} bytes selected in {1} groups):"; + // + // rawDataSectionLabel + // + this.rawDataSectionLabel.AutoSize = true; + this.rawDataSectionLabel.Location = new System.Drawing.Point(12, 63); + this.rawDataSectionLabel.Name = "rawDataSectionLabel"; + this.rawDataSectionLabel.Size = new System.Drawing.Size(64, 13); + this.rawDataSectionLabel.TabIndex = 2; + this.rawDataSectionLabel.Text = "Simple Data"; + // + // radioSingleBytes + // + this.radioSingleBytes.AutoSize = true; + this.radioSingleBytes.Location = new System.Drawing.Point(14, 89); + this.radioSingleBytes.Name = "radioSingleBytes"; + this.radioSingleBytes.Size = new System.Drawing.Size(82, 17); + this.radioSingleBytes.TabIndex = 4; + this.radioSingleBytes.TabStop = true; + this.radioSingleBytes.Text = "Single &bytes"; + this.radioSingleBytes.UseVisualStyleBackColor = true; + this.radioSingleBytes.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radio16BitBig + // + this.radio16BitBig.AutoSize = true; + this.radio16BitBig.Location = new System.Drawing.Point(14, 135); + this.radio16BitBig.Name = "radio16BitBig"; + this.radio16BitBig.Size = new System.Drawing.Size(137, 17); + this.radio16BitBig.TabIndex = 6; + this.radio16BitBig.TabStop = true; + this.radio16BitBig.Text = "16-bit words, big-endian"; + this.radio16BitBig.UseVisualStyleBackColor = true; + this.radio16BitBig.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radio16BitLittle + // + this.radio16BitLittle.AutoSize = true; + this.radio16BitLittle.Location = new System.Drawing.Point(14, 112); + this.radio16BitLittle.Name = "radio16BitLittle"; + this.radio16BitLittle.Size = new System.Drawing.Size(141, 17); + this.radio16BitLittle.TabIndex = 5; + this.radio16BitLittle.TabStop = true; + this.radio16BitLittle.Text = "16-bit words, little-endian"; + this.radio16BitLittle.UseVisualStyleBackColor = true; + this.radio16BitLittle.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radio24BitLittle + // + this.radio24BitLittle.AutoSize = true; + this.radio24BitLittle.Location = new System.Drawing.Point(14, 158); + this.radio24BitLittle.Name = "radio24BitLittle"; + this.radio24BitLittle.Size = new System.Drawing.Size(141, 17); + this.radio24BitLittle.TabIndex = 7; + this.radio24BitLittle.TabStop = true; + this.radio24BitLittle.Text = "24-bit words, little-endian"; + this.radio24BitLittle.UseVisualStyleBackColor = true; + this.radio24BitLittle.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radio32BitLittle + // + this.radio32BitLittle.AutoSize = true; + this.radio32BitLittle.Location = new System.Drawing.Point(14, 181); + this.radio32BitLittle.Name = "radio32BitLittle"; + this.radio32BitLittle.Size = new System.Drawing.Size(141, 17); + this.radio32BitLittle.TabIndex = 8; + this.radio32BitLittle.TabStop = true; + this.radio32BitLittle.Text = "32-bit words, little-endian"; + this.radio32BitLittle.UseVisualStyleBackColor = true; + this.radio32BitLittle.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radioSimpleDataBinary + // + this.radioSimpleDataBinary.AutoSize = true; + this.radioSimpleDataBinary.Location = new System.Drawing.Point(6, 64); + this.radioSimpleDataBinary.Name = "radioSimpleDataBinary"; + this.radioSimpleDataBinary.Size = new System.Drawing.Size(54, 17); + this.radioSimpleDataBinary.TabIndex = 4; + this.radioSimpleDataBinary.TabStop = true; + this.radioSimpleDataBinary.Text = "Binary"; + this.radioSimpleDataBinary.UseVisualStyleBackColor = true; + this.radioSimpleDataBinary.CheckedChanged += new System.EventHandler(this.SimpleDisplay_CheckedChanged); + // + // radioSimpleDataDecimal + // + this.radioSimpleDataDecimal.AutoSize = true; + this.radioSimpleDataDecimal.Location = new System.Drawing.Point(6, 41); + this.radioSimpleDataDecimal.Name = "radioSimpleDataDecimal"; + this.radioSimpleDataDecimal.Size = new System.Drawing.Size(63, 17); + this.radioSimpleDataDecimal.TabIndex = 3; + this.radioSimpleDataDecimal.TabStop = true; + this.radioSimpleDataDecimal.Text = "Decimal"; + this.radioSimpleDataDecimal.UseVisualStyleBackColor = true; + this.radioSimpleDataDecimal.CheckedChanged += new System.EventHandler(this.SimpleDisplay_CheckedChanged); + // + // radioSimpleDataHex + // + this.radioSimpleDataHex.AutoSize = true; + this.radioSimpleDataHex.Location = new System.Drawing.Point(6, 18); + this.radioSimpleDataHex.Name = "radioSimpleDataHex"; + this.radioSimpleDataHex.Size = new System.Drawing.Size(44, 17); + this.radioSimpleDataHex.TabIndex = 2; + this.radioSimpleDataHex.TabStop = true; + this.radioSimpleDataHex.Text = "Hex"; + this.radioSimpleDataHex.UseVisualStyleBackColor = true; + this.radioSimpleDataHex.CheckedChanged += new System.EventHandler(this.SimpleDisplay_CheckedChanged); + // + // radioDenseHex + // + this.radioDenseHex.AutoSize = true; + this.radioDenseHex.Location = new System.Drawing.Point(14, 247); + this.radioDenseHex.Name = "radioDenseHex"; + this.radioDenseHex.Size = new System.Drawing.Size(130, 17); + this.radioDenseHex.TabIndex = 11; + this.radioDenseHex.TabStop = true; + this.radioDenseHex.Text = "Densely-&packed bytes"; + this.radioDenseHex.UseVisualStyleBackColor = true; + this.radioDenseHex.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radioFill + // + this.radioFill.AutoSize = true; + this.radioFill.Location = new System.Drawing.Point(14, 270); + this.radioFill.Name = "radioFill"; + this.radioFill.Size = new System.Drawing.Size(88, 17); + this.radioFill.TabIndex = 12; + this.radioFill.TabStop = true; + this.radioFill.Text = "&Fill with value"; + this.radioFill.UseVisualStyleBackColor = true; + this.radioFill.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // horizontalLine1 + // + this.horizontalLine1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.horizontalLine1.Location = new System.Drawing.Point(12, 81); + this.horizontalLine1.Name = "horizontalLine1"; + this.horizontalLine1.Size = new System.Drawing.Size(500, 2); + this.horizontalLine1.TabIndex = 3; + // + // horizontalLine2 + // + this.horizontalLine2.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.horizontalLine2.Location = new System.Drawing.Point(12, 239); + this.horizontalLine2.Name = "horizontalLine2"; + this.horizontalLine2.Size = new System.Drawing.Size(320, 2); + this.horizontalLine2.TabIndex = 10; + // + // stringSectionLabel + // + this.stringSectionLabel.AutoSize = true; + this.stringSectionLabel.Location = new System.Drawing.Point(12, 308); + this.stringSectionLabel.Name = "stringSectionLabel"; + this.stringSectionLabel.Size = new System.Drawing.Size(34, 13); + this.stringSectionLabel.TabIndex = 13; + this.stringSectionLabel.Text = "String"; + // + // horizontalLine3 + // + this.horizontalLine3.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.horizontalLine3.Location = new System.Drawing.Point(12, 326); + this.horizontalLine3.Name = "horizontalLine3"; + this.horizontalLine3.Size = new System.Drawing.Size(320, 2); + this.horizontalLine3.TabIndex = 14; + // + // radioStringMixed + // + this.radioStringMixed.AutoSize = true; + this.radioStringMixed.Location = new System.Drawing.Point(14, 334); + this.radioStringMixed.Name = "radioStringMixed"; + this.radioStringMixed.Size = new System.Drawing.Size(257, 17); + this.radioStringMixed.TabIndex = 15; + this.radioStringMixed.TabStop = true; + this.radioStringMixed.Text = "Mixed ASCII ({0} bytes) and non-ASCII ({1} bytes)"; + this.radioStringMixed.UseVisualStyleBackColor = true; + this.radioStringMixed.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radioStringMixedReverse + // + this.radioStringMixedReverse.AutoSize = true; + this.radioStringMixedReverse.Location = new System.Drawing.Point(14, 357); + this.radioStringMixedReverse.Name = "radioStringMixedReverse"; + this.radioStringMixedReverse.Size = new System.Drawing.Size(275, 17); + this.radioStringMixedReverse.TabIndex = 16; + this.radioStringMixedReverse.TabStop = true; + this.radioStringMixedReverse.Text = "Reversed ASCII ({0} bytes) and non-ASCII ({1} bytes)"; + this.radioStringMixedReverse.UseVisualStyleBackColor = true; + this.radioStringMixedReverse.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radioStringNullTerm + // + this.radioStringNullTerm.AutoSize = true; + this.radioStringNullTerm.Location = new System.Drawing.Point(14, 381); + this.radioStringNullTerm.Name = "radioStringNullTerm"; + this.radioStringNullTerm.Size = new System.Drawing.Size(151, 17); + this.radioStringNullTerm.TabIndex = 17; + this.radioStringNullTerm.TabStop = true; + this.radioStringNullTerm.Text = "Null-terminated strings ({0})"; + this.radioStringNullTerm.UseVisualStyleBackColor = true; + this.radioStringNullTerm.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radioStringLen8 + // + this.radioStringLen8.AutoSize = true; + this.radioStringLen8.Location = new System.Drawing.Point(14, 405); + this.radioStringLen8.Name = "radioStringLen8"; + this.radioStringLen8.Size = new System.Drawing.Size(197, 17); + this.radioStringLen8.TabIndex = 18; + this.radioStringLen8.TabStop = true; + this.radioStringLen8.Text = "Strings prefixed with 8-bit length ({0})"; + this.radioStringLen8.UseVisualStyleBackColor = true; + this.radioStringLen8.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radioStringLen16 + // + this.radioStringLen16.AutoSize = true; + this.radioStringLen16.Location = new System.Drawing.Point(14, 429); + this.radioStringLen16.Name = "radioStringLen16"; + this.radioStringLen16.Size = new System.Drawing.Size(203, 17); + this.radioStringLen16.TabIndex = 19; + this.radioStringLen16.TabStop = true; + this.radioStringLen16.Text = "Strings prefixed with 16-bit length ({0})"; + this.radioStringLen16.UseVisualStyleBackColor = true; + this.radioStringLen16.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radioStringDci + // + this.radioStringDci.AutoSize = true; + this.radioStringDci.Location = new System.Drawing.Point(14, 453); + this.radioStringDci.Name = "radioStringDci"; + this.radioStringDci.Size = new System.Drawing.Size(170, 17); + this.radioStringDci.TabIndex = 20; + this.radioStringDci.TabStop = true; + this.radioStringDci.Text = "Dextral character inverted ({0})"; + this.radioStringDci.UseVisualStyleBackColor = true; + this.radioStringDci.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // symbolEntryTextBox + // + this.symbolEntryTextBox.Location = new System.Drawing.Point(108, 62); + this.symbolEntryTextBox.Name = "symbolEntryTextBox"; + this.symbolEntryTextBox.Size = new System.Drawing.Size(200, 20); + this.symbolEntryTextBox.TabIndex = 1; + this.symbolEntryTextBox.TextChanged += new System.EventHandler(this.symbolEntryTextBox_TextChanged); + // + // symbolPartPanel + // + this.symbolPartPanel.Controls.Add(this.radioSymbolPartBank); + this.symbolPartPanel.Controls.Add(this.radioSymbolPartHigh); + this.symbolPartPanel.Controls.Add(this.radioSymbolPartLow); + this.symbolPartPanel.Location = new System.Drawing.Point(108, 86); + this.symbolPartPanel.Name = "symbolPartPanel"; + this.symbolPartPanel.Size = new System.Drawing.Size(200, 20); + this.symbolPartPanel.TabIndex = 27; + // + // radioSymbolPartBank + // + this.radioSymbolPartBank.AutoSize = true; + this.radioSymbolPartBank.Location = new System.Drawing.Point(103, 1); + this.radioSymbolPartBank.Name = "radioSymbolPartBank"; + this.radioSymbolPartBank.Size = new System.Drawing.Size(50, 17); + this.radioSymbolPartBank.TabIndex = 2; + this.radioSymbolPartBank.TabStop = true; + this.radioSymbolPartBank.Text = "Bank"; + this.radioSymbolPartBank.UseVisualStyleBackColor = true; + this.radioSymbolPartBank.CheckedChanged += new System.EventHandler(this.PartGroup_CheckedChanged); + // + // radioSymbolPartHigh + // + this.radioSymbolPartHigh.AutoSize = true; + this.radioSymbolPartHigh.Location = new System.Drawing.Point(52, 1); + this.radioSymbolPartHigh.Name = "radioSymbolPartHigh"; + this.radioSymbolPartHigh.Size = new System.Drawing.Size(47, 17); + this.radioSymbolPartHigh.TabIndex = 1; + this.radioSymbolPartHigh.TabStop = true; + this.radioSymbolPartHigh.Text = "High"; + this.radioSymbolPartHigh.UseVisualStyleBackColor = true; + this.radioSymbolPartHigh.CheckedChanged += new System.EventHandler(this.PartGroup_CheckedChanged); + // + // radioSymbolPartLow + // + this.radioSymbolPartLow.AutoSize = true; + this.radioSymbolPartLow.Location = new System.Drawing.Point(4, 1); + this.radioSymbolPartLow.Name = "radioSymbolPartLow"; + this.radioSymbolPartLow.Size = new System.Drawing.Size(45, 17); + this.radioSymbolPartLow.TabIndex = 0; + this.radioSymbolPartLow.TabStop = true; + this.radioSymbolPartLow.Text = "Low"; + this.radioSymbolPartLow.UseVisualStyleBackColor = true; + this.radioSymbolPartLow.CheckedChanged += new System.EventHandler(this.PartGroup_CheckedChanged); + // + // radioSimpleDataAscii + // + this.radioSimpleDataAscii.AutoSize = true; + this.radioSimpleDataAscii.Location = new System.Drawing.Point(6, 87); + this.radioSimpleDataAscii.Name = "radioSimpleDataAscii"; + this.radioSimpleDataAscii.Size = new System.Drawing.Size(52, 17); + this.radioSimpleDataAscii.TabIndex = 5; + this.radioSimpleDataAscii.TabStop = true; + this.radioSimpleDataAscii.Text = "ASCII"; + this.radioSimpleDataAscii.UseVisualStyleBackColor = true; + this.radioSimpleDataAscii.CheckedChanged += new System.EventHandler(this.SimpleDisplay_CheckedChanged); + // + // radioDefaultFormat + // + this.radioDefaultFormat.AutoSize = true; + this.radioDefaultFormat.Location = new System.Drawing.Point(14, 30); + this.radioDefaultFormat.Name = "radioDefaultFormat"; + this.radioDefaultFormat.Size = new System.Drawing.Size(59, 17); + this.radioDefaultFormat.TabIndex = 1; + this.radioDefaultFormat.TabStop = true; + this.radioDefaultFormat.Text = "&Default"; + this.radioDefaultFormat.UseVisualStyleBackColor = true; + this.radioDefaultFormat.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // simpleDisplayAsGroupBox + // + this.simpleDisplayAsGroupBox.Controls.Add(this.radioSimpleDataAddress); + this.simpleDisplayAsGroupBox.Controls.Add(this.radioSimpleDataSymbolic); + this.simpleDisplayAsGroupBox.Controls.Add(this.radioSimpleDataAscii); + this.simpleDisplayAsGroupBox.Controls.Add(this.radioSimpleDataHex); + this.simpleDisplayAsGroupBox.Controls.Add(this.symbolPartPanel); + this.simpleDisplayAsGroupBox.Controls.Add(this.radioSimpleDataBinary); + this.simpleDisplayAsGroupBox.Controls.Add(this.symbolEntryTextBox); + this.simpleDisplayAsGroupBox.Controls.Add(this.radioSimpleDataDecimal); + this.simpleDisplayAsGroupBox.Location = new System.Drawing.Point(190, 84); + this.simpleDisplayAsGroupBox.Name = "simpleDisplayAsGroupBox"; + this.simpleDisplayAsGroupBox.Size = new System.Drawing.Size(320, 114); + this.simpleDisplayAsGroupBox.TabIndex = 24; + this.simpleDisplayAsGroupBox.TabStop = false; + this.simpleDisplayAsGroupBox.Text = "Display As..."; + // + // radioSimpleDataAddress + // + this.radioSimpleDataAddress.AutoSize = true; + this.radioSimpleDataAddress.Location = new System.Drawing.Point(89, 18); + this.radioSimpleDataAddress.Name = "radioSimpleDataAddress"; + this.radioSimpleDataAddress.Size = new System.Drawing.Size(63, 17); + this.radioSimpleDataAddress.TabIndex = 6; + this.radioSimpleDataAddress.TabStop = true; + this.radioSimpleDataAddress.Text = "&Address"; + this.radioSimpleDataAddress.UseVisualStyleBackColor = true; + // + // radioSimpleDataSymbolic + // + this.radioSimpleDataSymbolic.AutoSize = true; + this.radioSimpleDataSymbolic.Location = new System.Drawing.Point(89, 41); + this.radioSimpleDataSymbolic.Name = "radioSimpleDataSymbolic"; + this.radioSimpleDataSymbolic.Size = new System.Drawing.Size(115, 17); + this.radioSimpleDataSymbolic.TabIndex = 7; + this.radioSimpleDataSymbolic.TabStop = true; + this.radioSimpleDataSymbolic.Text = "&Symbolic reference"; + this.radioSimpleDataSymbolic.UseVisualStyleBackColor = true; + this.radioSimpleDataSymbolic.CheckedChanged += new System.EventHandler(this.SimpleDisplay_CheckedChanged); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 221); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(54, 13); + this.label1.TabIndex = 9; + this.label1.Text = "Bulk Data"; + // + // EditData + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(525, 496); + this.Controls.Add(this.label1); + this.Controls.Add(this.simpleDisplayAsGroupBox); + this.Controls.Add(this.radioDefaultFormat); + this.Controls.Add(this.radioStringDci); + this.Controls.Add(this.radioStringLen16); + this.Controls.Add(this.radioStringLen8); + this.Controls.Add(this.radioStringNullTerm); + this.Controls.Add(this.radioStringMixedReverse); + this.Controls.Add(this.radioStringMixed); + this.Controls.Add(this.stringSectionLabel); + this.Controls.Add(this.horizontalLine1); + this.Controls.Add(this.horizontalLine2); + this.Controls.Add(this.horizontalLine3); + this.Controls.Add(this.radioFill); + this.Controls.Add(this.radioDenseHex); + this.Controls.Add(this.radio32BitLittle); + this.Controls.Add(this.radio24BitLittle); + this.Controls.Add(this.radio16BitLittle); + this.Controls.Add(this.radio16BitBig); + this.Controls.Add(this.radioSingleBytes); + this.Controls.Add(this.rawDataSectionLabel); + this.Controls.Add(this.selectFormatLabel); + this.Controls.Add(this.okButton); + this.Controls.Add(this.cancelButton); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditData"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Data"; + this.Load += new System.EventHandler(this.EditData_Load); + this.Shown += new System.EventHandler(this.EditData_Shown); + this.symbolPartPanel.ResumeLayout(false); + this.symbolPartPanel.PerformLayout(); + this.simpleDisplayAsGroupBox.ResumeLayout(false); + this.simpleDisplayAsGroupBox.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Label selectFormatLabel; + private System.Windows.Forms.Label rawDataSectionLabel; + private System.Windows.Forms.RadioButton radioSingleBytes; + private System.Windows.Forms.RadioButton radio16BitBig; + private System.Windows.Forms.RadioButton radio16BitLittle; + private System.Windows.Forms.RadioButton radio24BitLittle; + private System.Windows.Forms.RadioButton radio32BitLittle; + private System.Windows.Forms.RadioButton radioSimpleDataBinary; + private System.Windows.Forms.RadioButton radioSimpleDataDecimal; + private System.Windows.Forms.RadioButton radioSimpleDataHex; + private System.Windows.Forms.RadioButton radioDenseHex; + private System.Windows.Forms.RadioButton radioFill; + private System.Windows.Forms.Label horizontalLine1; + private System.Windows.Forms.Label horizontalLine2; + private System.Windows.Forms.Label stringSectionLabel; + private System.Windows.Forms.Label horizontalLine3; + private System.Windows.Forms.RadioButton radioStringMixed; + private System.Windows.Forms.RadioButton radioStringMixedReverse; + private System.Windows.Forms.RadioButton radioStringNullTerm; + private System.Windows.Forms.RadioButton radioStringLen8; + private System.Windows.Forms.RadioButton radioStringLen16; + private System.Windows.Forms.RadioButton radioStringDci; + private System.Windows.Forms.TextBox symbolEntryTextBox; + private System.Windows.Forms.Panel symbolPartPanel; + private System.Windows.Forms.RadioButton radioSymbolPartBank; + private System.Windows.Forms.RadioButton radioSymbolPartHigh; + private System.Windows.Forms.RadioButton radioSymbolPartLow; + private System.Windows.Forms.RadioButton radioSimpleDataAscii; + private System.Windows.Forms.RadioButton radioDefaultFormat; + private System.Windows.Forms.GroupBox simpleDisplayAsGroupBox; + private System.Windows.Forms.RadioButton radioSimpleDataSymbolic; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.RadioButton radioSimpleDataAddress; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/EditData.cs b/SourceGen/AppForms/EditData.cs new file mode 100644 index 0000000..c3d2e3f --- /dev/null +++ b/SourceGen/AppForms/EditData.cs @@ -0,0 +1,953 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Windows.Forms; + +using CommonUtil; + +namespace SourceGen.AppForms { + public partial class EditData : Form { + /// + /// Result set that describes the formatting to perform. Not all regions will have + /// the same format, e.g. the "mixed ASCII" mode will alternate strings and bytes + /// (rather than a dedicated "mixed ASCII" format type). + /// + public SortedList Results { get; private set; } + + /// + /// Selected offsets. An otherwise contiguous range of offsets can be broken up + /// by user-specified labels and address discontinuities, so this needs to be + /// processed by range. + /// + public TypedRangeSet Selection { private get; set; } + + /// + /// FormatDescriptor from the first offset. May be null if the offset doesn't + /// have a format descriptor specified. This will be used to configure the + /// dialog controls if the format is suited to the selection. The goal is to + /// make single-item editing work as expected. + /// + public FormatDescriptor FirstFormatDescriptor { private get; set; } + + /// + /// Raw file data. + /// + private byte[] mFileData; + + /// + /// Symbol table to use when resolving symbolic values. + /// + private SymbolTable mSymbolTable; + + /// + /// Formatter to use when displaying addresses and hex values. + /// + private Asm65.Formatter mFormatter; + + /// + /// Set this during initial control configuration, so we know to ignore the CheckedChanged + /// events. + /// + private bool mIsInitialSetup; + + /// + /// Set to true if, during the initial setup, the format defined by FirstFormatDescriptor + /// was unavailable. + /// + private bool mPreferredFormatUnavailable; + + + public EditData(byte[] fileData, SymbolTable symbolTable, Asm65.Formatter formatter) { + InitializeComponent(); + + mFileData = fileData; + mSymbolTable = symbolTable; + mFormatter = formatter; + + //Results = new List(); + } + + private void EditData_Load(object sender, EventArgs e) { + DateTime startWhen = DateTime.Now; + + mIsInitialSetup = true; + + // Determine which of the various options is suitable for the selected offsets. + // Disable any radio buttons that won't work. + AnalyzeRanges(); + + // Configure the dialog from the FormatDescriptor, if one is available. + Debug.WriteLine("First FD: " + FirstFormatDescriptor); + SetControlsFromDescriptor(FirstFormatDescriptor); + + if (mPreferredFormatUnavailable) { + // This can happen when e.g. a bunch of stuff is formatted as null-terminated + // strings. We don't recognize a lone zero as a string, but we allow it if + // it's next to a bunch of others. If you come back later and try to format + // just that one byte, you end up here. + // TODO(maybe): make it more obvious what's going on? + Debug.WriteLine("NOTE: preferred format unavailable"); + } + + mIsInitialSetup = false; + UpdateControls(); + + Debug.WriteLine("EditData dialog load time: " + + (DateTime.Now - startWhen).TotalMilliseconds + " ms"); + } + + private void EditData_Shown(object sender, EventArgs e) { + // Start with the focus in the text box if the initial format allows for a + // symbolic reference. This way they can start typing immediately. + if (simpleDisplayAsGroupBox.Enabled) { + symbolEntryTextBox.Focus(); + } + } + + /// + /// Handles CheckedChanged event for all radio buttons in main group. This will + /// fire twice when a radio button is clicked (once to un-check the old, once + /// to check the new). + /// + private void MainGroup_CheckedChanged(object sender, EventArgs e) { + // Enable/disable the style group and the low/high/bank radio group. + // Update preview window. + UpdateControls(); + } + + /// + /// Handles CheckedChanged event for radio buttons in the simple-data "display as" + /// group box. + /// + private void SimpleDisplay_CheckedChanged(object sender, EventArgs e) { + // Enable/disable the low/high/bank radio group. + UpdateControls(); + } + + /// + /// Handles CheckedChanged event for all radio buttons in symbol-part group. + /// + private void PartGroup_CheckedChanged(object sender, EventArgs e) { + // not currently using a preview window; could add one for single items? + } + + private void symbolEntryTextBox_TextChanged(object sender, EventArgs e) { + // Make sure Symbol is checked if they're typing text in. + Debug.Assert(radioSimpleDataSymbolic.Enabled); + radioSimpleDataSymbolic.Checked = true; + // Update OK button based on symbol validity. + UpdateControls(); + } + + private void okButton_Click(object sender, EventArgs e) { + CreateDescriptorListFromControls(); + FormatDescriptor.DebugDumpSortedList(Results); + } + + /// + /// Updates all of the controls to reflect the current internal state. + /// + private void UpdateControls() { + if (mIsInitialSetup) { + return; + } + + // Configure the simple data "display as" style box. + bool wantStyle = false; + int simpleWidth = -1; + bool isBigEndian = false; + if (radioSingleBytes.Checked) { + wantStyle = true; + simpleWidth = 1; + } else if (radio16BitLittle.Checked) { + wantStyle = true; + simpleWidth = 2; + } else if (radio16BitBig.Checked) { + wantStyle = true; + simpleWidth = 2; + isBigEndian = true; + } else if (radio24BitLittle.Checked) { + wantStyle = true; + simpleWidth = 3; + } else if (radio32BitLittle.Checked) { + wantStyle = true; + simpleWidth = 4; + } + bool focusOnSymbol = !simpleDisplayAsGroupBox.Enabled && wantStyle; + simpleDisplayAsGroupBox.Enabled = wantStyle; + if (wantStyle) { + // TODO(soon): compute on first need and save results; this is getting called + // 2x as radio buttons are hit, and might be slow on large data sets + radioSimpleDataAscii.Enabled = IsRawAsciiCompatible(simpleWidth, isBigEndian); + } + + // Enable the symbolic reference entry box if the "display as" group is enabled. + // That way instead of "click 16-bit", "click symbol", "enter symbol", the user + // can skip the second step. + symbolEntryTextBox.Enabled = simpleDisplayAsGroupBox.Enabled; + symbolPartPanel.Enabled = radioSimpleDataSymbolic.Checked; + + // If we just enabled the group box, set the focus on the symbol entry box. This + // removes another click from the steps, though it's a bit aggressive if you're + // trying to arrow your way through the items. + if (focusOnSymbol) { + symbolEntryTextBox.Focus(); + } + + bool isOk = true; + if (radioSimpleDataSymbolic.Checked) { + // Just check for correct format. References to non-existent labels are allowed. + isOk = Asm65.Label.ValidateLabel(symbolEntryTextBox.Text); + + // Actually, let's discourage references to auto-labels. + if (isOk && mSymbolTable.TryGetValue(symbolEntryTextBox.Text, out Symbol sym)) { + isOk = sym.SymbolSource != Symbol.Source.Auto; + } + } + okButton.Enabled = isOk; + } + + /// + /// Analyzes the selection to see which data formatting options are suitable. + /// Disables radio buttons and updates labels. + /// + /// Call this once, when the dialog is first loaded. + /// + private void AnalyzeRanges() { + Debug.Assert(Selection.Count != 0); + + string fmt = (Selection.RangeCount == 1) ? + Properties.Resources.FMT_FORMAT_SINGLE_GROUP : + Properties.Resources.FMT_FORMAT_MULTIPLE_GROUPS; + selectFormatLabel.Text = string.Format(fmt, Selection.Count, Selection.RangeCount); + + IEnumerator iter = Selection.RangeListIterator; + + int mixedAsciiOkCount = 0; + int mixedAsciiNotCount = 0; + int nullTermStringCount = 0; + int len8StringCount = 0; + int len16StringCount = 0; + int dciStringCount = 0; + //int revDciStringCount = 0; + + // For each range, check to see if the data within qualifies for the various + // options. If any of them fail to meet the criteria, the option is disabled + // for all ranges. + while (iter.MoveNext()) { + TypedRangeSet.TypedRange rng = iter.Current; + Debug.WriteLine("Testing [" + rng.Low + ", " + rng.High + "]"); + + // Start with the easy ones. Single-byte and dense are always enabled. + + int count = rng.High - rng.Low + 1; + Debug.Assert(count > 0); + if ((count & 0x01) != 0) { + // not divisible by 2, disallow 16-bit entries + radio16BitLittle.Enabled = false; + radio16BitBig.Enabled = false; + } + if ((count & 0x03) != 0) { + // not divisible by 4, disallow 32-bit entries + radio32BitLittle.Enabled = false; + } + if ((count / 3) * 3 != count) { + // not divisible by 3, disallow 24-bit entries + radio24BitLittle.Enabled = false; + } + + + // Check for run of bytes (2 or more of the same thing). Remember that + // we check this one region at a time, and each region could have different + // bytes, but so long as the bytes are all the same within a region we're good. + if (radioFill.Enabled && count > 1 && + DataAnalysis.RecognizeRun(mFileData, rng.Low, rng.High) == count) { + // LGTM + } else { + radioFill.Enabled = false; + } + + // See if there's enough string data to make it worthwhile. We use an + // arbitrary threshold of 2+ ASCII characters, and require twice as many + // ASCII as non-ASCII. We arbitrarily require the strings to be either + // high or low ASCII, and treat the other as non-ASCII. (We could relax + // this -- we generate separate items for each string and non-ASCII chunk -- + // but I'm trying to hide the option when the buffer doesn't really seem + // to be holding strings. Could replace with some sort of minimum string + // length requirement?) + if (radioStringMixed.Enabled) { + int asciiCount; + DataAnalysis.CountAsciiBytes(mFileData, rng.Low, rng.High, + out int lowAscii, out int highAscii, out int nonAscii); + if (highAscii > lowAscii) { + asciiCount = highAscii; + nonAscii += lowAscii; + } else { + asciiCount = lowAscii; + nonAscii += highAscii; + } + + if (asciiCount >= 2 && asciiCount >= nonAscii * 2) { + // Looks good + mixedAsciiOkCount += asciiCount; + mixedAsciiNotCount += nonAscii; + } else { + // Fail + radioStringMixed.Enabled = false; + radioStringMixedReverse.Enabled = false; + mixedAsciiOkCount = mixedAsciiNotCount = -1; + } + } + + // Check for null-terminated strings. Zero-length strings are allowed, but + // not counted -- we want to have some actual character data. Individual + // strings need to be entirely high-ASCII or low-ASCII, but not all strings + // in a region have to be the same. + if (radioStringNullTerm.Enabled) { + int strCount = DataAnalysis.RecognizeNullTerminatedStrings(mFileData, + rng.Low, rng.High); + if (strCount > 0) { + nullTermStringCount += strCount; + } else { + radioStringNullTerm.Enabled = false; + nullTermStringCount = -1; + } + } + + // Check for strings prefixed with an 8-bit length. + if (radioStringLen8.Enabled) { + int strCount = DataAnalysis.RecognizeLen8Strings(mFileData, rng.Low, rng.High); + if (strCount > 0) { + len8StringCount += strCount; + } else { + radioStringLen8.Enabled = false; + len8StringCount = -1; + } + } + + // Check for strings prefixed with a 16-bit length. + if (radioStringLen16.Enabled) { + int strCount = DataAnalysis.RecognizeLen16Strings(mFileData, rng.Low, rng.High); + if (strCount > 0) { + len16StringCount += strCount; + } else { + radioStringLen16.Enabled = false; + len16StringCount = -1; + } + } + + // Check for DCI strings. All strings within a single range must have the + // same "polarity", e.g. low ASCII terminated by high ASCII. + if (radioStringDci.Enabled) { + int strCount = DataAnalysis.RecognizeDciStrings(mFileData, rng.Low, rng.High); + if (strCount > 0) { + dciStringCount += strCount; + } else { + radioStringDci.Enabled = false; + dciStringCount = -1; + } + } + + //// Check for reverse DCI strings. All strings within a single range must have the + //// same "polarity", e.g. low ASCII terminated by high ASCII. + //if (radioStringDciReverse.Enabled) { + // int strCount = DataAnalysis.RecognizeReverseDciStrings(mFileData, + // rng.Low, rng.High); + // if (strCount > 0) { + // revDciStringCount += strCount; + // } else { + // radioStringDciReverse.Enabled = false; + // revDciStringCount = -1; + // } + //} + } + + // Update the dialog with string and character counts, summed across all regions. + + if (mixedAsciiOkCount > 0) { + Debug.Assert(radioStringMixed.Enabled); + radioStringMixed.Text = string.Format(radioStringMixed.Text, + mixedAsciiOkCount, mixedAsciiNotCount); + radioStringMixedReverse.Text = string.Format(radioStringMixedReverse.Text, + mixedAsciiOkCount, mixedAsciiNotCount); + } else { + Debug.Assert(!radioStringMixed.Enabled); + radioStringMixed.Text = string.Format(radioStringMixed.Text, "xx", "xx"); + radioStringMixedReverse.Text = string.Format(radioStringMixedReverse.Text, + "xx", "xx"); + } + + if (nullTermStringCount > 0) { + Debug.Assert(radioStringNullTerm.Enabled); + radioStringNullTerm.Text = string.Format(radioStringNullTerm.Text, nullTermStringCount); + } else { + Debug.Assert(!radioStringNullTerm.Enabled); + radioStringNullTerm.Text = string.Format(radioStringNullTerm.Text, "xx"); + } + + if (len8StringCount > 0) { + Debug.Assert(radioStringLen8.Enabled); + radioStringLen8.Text = string.Format(radioStringLen8.Text, len8StringCount); + } else { + Debug.Assert(!radioStringLen8.Enabled); + radioStringLen8.Text = string.Format(radioStringLen8.Text, "xx"); + } + + if (len16StringCount > 0) { + Debug.Assert(radioStringLen16.Enabled); + radioStringLen16.Text = string.Format(radioStringLen16.Text, len16StringCount); + } else { + Debug.Assert(!radioStringLen16.Enabled); + radioStringLen16.Text = string.Format(radioStringLen16.Text, "xx"); + } + + if (dciStringCount > 0) { + Debug.Assert(radioStringDci.Enabled); + radioStringDci.Text = string.Format(radioStringDci.Text, dciStringCount); + } else { + Debug.Assert(!radioStringDci.Enabled); + radioStringDci.Text = string.Format(radioStringDci.Text, "xx"); + } + + //if (revDciStringCount > 0) { + // Debug.Assert(radioStringDciReverse.Enabled); + // radioStringDciReverse.Text = + // string.Format(radioStringDciReverse.Text, revDciStringCount); + //} else { + // Debug.Assert(!radioStringDciReverse.Enabled); + // radioStringDciReverse.Text = string.Format(radioStringDciReverse.Text, "xx"); + //} + } + + /// + /// Determines whether the data in the buffer can be represented as ASCII values. + /// Using ".DD1 'A'" for 0x41 is obvious, but we also allow ".DD2 'A'" for + /// 0x41 0x00. 16-bit character constants are more likely as intermediate + /// operands, but could be found in data areas. + /// + /// High and low ASCII are allowed, and may be freely mixed. + /// + /// Testing explicitly is probably excessive, and possibly counter-productive if + /// the user is trying to flag an area that is a mix of ASCII and non-ASCII and + /// just wants hex for the rest, but we'll give it a try. + /// + /// Number of bytes per character. + /// Word endian-ness. + /// True if data in all regions can be represented as high or low ASCII. + private bool IsRawAsciiCompatible(int wordWidth, bool isBigEndian) { + IEnumerator iter = Selection.RangeListIterator; + while (iter.MoveNext()) { + TypedRangeSet.TypedRange rng = iter.Current; + Debug.Assert(((rng.High - rng.Low + 1) / wordWidth) * wordWidth == + rng.High - rng.Low + 1); + for (int i = rng.Low; i <= rng.High; i += wordWidth) { + int val = RawData.GetWord(mFileData, rng.Low, wordWidth, isBigEndian); + if (val < 0x20 || (val >= 0x7f && val < 0xa0) || val >= 0xff) { + // bad value, fail + return false; + } + } + } + return true; + } + + /// + /// Configures the dialog controls based on the provided format descriptor. If + /// the desired options are unavailable, a suitable default is selected instead. + /// + /// FormatDescriptor to use. + private void SetControlsFromDescriptor(FormatDescriptor dfd) { + Debug.Assert(mIsInitialSetup); + radioSimpleDataHex.Checked = true; + radioSymbolPartLow.Checked = true; + + if (dfd == null) { + radioDefaultFormat.Checked = true; + return; + } + + RadioButton preferredFormat; + + switch (dfd.FormatType) { + case FormatDescriptor.Type.NumericLE: + case FormatDescriptor.Type.NumericBE: + switch (dfd.Length) { + case 1: + preferredFormat = radioSingleBytes; + break; + case 2: + preferredFormat = + (dfd.FormatType == FormatDescriptor.Type.NumericLE ? + radio16BitLittle : radio16BitBig); + break; + case 3: + preferredFormat = radio24BitLittle; + break; + case 4: + preferredFormat = radio32BitLittle; + break; + default: + Debug.Assert(false); + preferredFormat = radioDefaultFormat; + break; + } + if (preferredFormat.Enabled) { + switch (dfd.FormatSubType) { + case FormatDescriptor.SubType.None: + case FormatDescriptor.SubType.Hex: + radioSimpleDataHex.Checked = true; + break; + case FormatDescriptor.SubType.Decimal: + radioSimpleDataDecimal.Checked = true; + break; + case FormatDescriptor.SubType.Binary: + radioSimpleDataBinary.Checked = true; + break; + case FormatDescriptor.SubType.Ascii: + radioSimpleDataAscii.Checked = true; + break; + case FormatDescriptor.SubType.Address: + radioSimpleDataAddress.Checked = true; + break; + case FormatDescriptor.SubType.Symbol: + radioSimpleDataSymbolic.Checked = true; + switch (dfd.SymbolRef.ValuePart) { + case WeakSymbolRef.Part.Low: + radioSymbolPartLow.Checked = true; + break; + case WeakSymbolRef.Part.High: + radioSymbolPartHigh.Checked = true; + break; + case WeakSymbolRef.Part.Bank: + radioSymbolPartBank.Checked = true; + break; + default: + Debug.Assert(false); + break; + } + Debug.Assert(dfd.HasSymbol); + symbolEntryTextBox.Text = dfd.SymbolRef.Label; + break; + default: + Debug.Assert(false); + break; + } + } else { + // preferred format not enabled; leave Hex/Low checked + } + break; + case FormatDescriptor.Type.String: + switch (dfd.FormatSubType) { + case FormatDescriptor.SubType.None: + preferredFormat = radioStringMixed; + break; + case FormatDescriptor.SubType.Reverse: + preferredFormat = radioStringMixedReverse; + break; + case FormatDescriptor.SubType.CString: + preferredFormat = radioStringNullTerm; + break; + case FormatDescriptor.SubType.L8String: + preferredFormat = radioStringLen8; + break; + case FormatDescriptor.SubType.L16String: + preferredFormat = radioStringLen16; + break; + case FormatDescriptor.SubType.Dci: + preferredFormat = radioStringDci; + break; + case FormatDescriptor.SubType.DciReverse: + preferredFormat = radioDefaultFormat; + break; + default: + Debug.Assert(false); + preferredFormat = radioDefaultFormat; + break; + } + break; + case FormatDescriptor.Type.Dense: + preferredFormat = radioDenseHex; + break; + case FormatDescriptor.Type.Fill: + preferredFormat = radioFill; + break; + default: + // Should not be here. + Debug.Assert(false); + preferredFormat = radioDefaultFormat; + break; + } + + if (preferredFormat.Enabled) { + preferredFormat.Checked = true; + } else { + mPreferredFormatUnavailable = true; + radioDefaultFormat.Checked = true; + } + } + + /// + /// Creates a list of FormatDescriptors, based on the current control configuration. + /// + /// The entries in the list are guaranteed to be sorted by start address and not + /// overlap. + /// + /// We assume that whatever the control gives us is correct, e.g. it's not going + /// to tell us to put a buffer full of zeroes into a DCI string. + /// + /// Result list. + private void CreateDescriptorListFromControls() { + FormatDescriptor.Type type = FormatDescriptor.Type.Default; + FormatDescriptor.SubType subType = FormatDescriptor.SubType.None; + WeakSymbolRef symbolRef = null; + int chunkLength = -1; + + // Decode the "display as" panel, if it's relevant. + if (radioSimpleDataHex.Enabled) { + if (radioSimpleDataHex.Checked) { + subType = FormatDescriptor.SubType.Hex; + } else if (radioSimpleDataDecimal.Checked) { + subType = FormatDescriptor.SubType.Decimal; + } else if (radioSimpleDataBinary.Checked) { + subType = FormatDescriptor.SubType.Binary; + } else if (radioSimpleDataAscii.Checked) { + subType = FormatDescriptor.SubType.Ascii; + } else if (radioSimpleDataAddress.Checked) { + subType = FormatDescriptor.SubType.Address; + } else if (radioSimpleDataSymbolic.Checked) { + WeakSymbolRef.Part part; + if (radioSymbolPartLow.Checked) { + part = WeakSymbolRef.Part.Low; + } else if (radioSymbolPartHigh.Checked) { + part = WeakSymbolRef.Part.High; + } else if (radioSymbolPartBank.Checked) { + part = WeakSymbolRef.Part.Bank; + } else { + Debug.Assert(false); + part = WeakSymbolRef.Part.Low; + } + subType = FormatDescriptor.SubType.Symbol; + symbolRef = new WeakSymbolRef(symbolEntryTextBox.Text, part); + } else { + Debug.Assert(false); + } + } else { + subType = 0; // set later, or doesn't matter + } + + // Decode the main format. + if (radioDefaultFormat.Checked) { + // Default/None; note this would create a multi-byte Default format, which isn't + // really allowed. What we actually want to do is remove the explicit formatting + // from all spanned offsets, so we use a dedicated type for that. + type = FormatDescriptor.Type.REMOVE; + } else if (radioSingleBytes.Checked) { + type = FormatDescriptor.Type.NumericLE; + chunkLength = 1; + } else if (radio16BitLittle.Checked) { + type = FormatDescriptor.Type.NumericLE; + chunkLength = 2; + } else if (radio16BitBig.Checked) { + type = FormatDescriptor.Type.NumericBE; + chunkLength = 2; + } else if (radio24BitLittle.Checked) { + type = FormatDescriptor.Type.NumericLE; + chunkLength = 3; + } else if (radio32BitLittle.Checked) { + type = FormatDescriptor.Type.NumericLE; + chunkLength = 4; + } else if (radioDenseHex.Checked) { + type = FormatDescriptor.Type.Dense; + } else if (radioFill.Checked) { + type = FormatDescriptor.Type.Fill; + } else if (radioStringMixed.Checked) { + type = FormatDescriptor.Type.String; + } else if (radioStringMixedReverse.Checked) { + type = FormatDescriptor.Type.String; + subType = FormatDescriptor.SubType.Reverse; + } else if (radioStringNullTerm.Checked) { + type = FormatDescriptor.Type.String; + subType = FormatDescriptor.SubType.CString; + } else if (radioStringLen8.Checked) { + type = FormatDescriptor.Type.String; + subType = FormatDescriptor.SubType.L8String; + } else if (radioStringLen16.Checked) { + type = FormatDescriptor.Type.String; + subType = FormatDescriptor.SubType.L16String; + } else if (radioStringDci.Checked) { + type = FormatDescriptor.Type.String; + subType = FormatDescriptor.SubType.Dci; + //} else if (radioStringDciReverse.Checked) { + // type = FormatDescriptor.Type.String; + // subType = FormatDescriptor.SubType.DciReverse; + } else { + Debug.Assert(false); + // default/none + } + + + Results = new SortedList(); + + IEnumerator iter = Selection.RangeListIterator; + while (iter.MoveNext()) { + TypedRangeSet.TypedRange rng = iter.Current; + + if (type == FormatDescriptor.Type.String) { + // We want to create one FormatDescriptor object per string. That way + // each string gets its own line. + if ((subType == FormatDescriptor.SubType.None || + subType == FormatDescriptor.SubType.Reverse)) { + CreateMixedStringEntries(rng.Low, rng.High, subType); + } else if (subType == FormatDescriptor.SubType.CString) { + CreateCStringEntries(rng.Low, rng.High, subType); + } else if (subType == FormatDescriptor.SubType.L8String || + subType == FormatDescriptor.SubType.L16String) { + CreateLengthStringEntries(rng.Low, rng.High, subType); + } else if (subType == FormatDescriptor.SubType.Dci || + subType == FormatDescriptor.SubType.DciReverse) { + CreateDciStringEntries(rng.Low, rng.High, subType); + } else { + Debug.Assert(false); + CreateMixedStringEntries(rng.Low, rng.High, subType); // shrug + } + } else { + CreateSimpleEntries(type, subType, chunkLength, symbolRef, rng.Low, rng.High); + } + } + } + + /// + /// Creates one or more FormatDescriptor entries for the specified range, adding them + /// to the Results list. + /// + /// This will either create one entry that spans the entire range (for e.g. strings + /// and bulk data), or create equal-sized chunks. + /// + /// Region data type. + /// Region data sub-type. + /// Length of a chunk, or -1 for full buffer. + /// Symbol reference, or null if not applicable. + /// Offset of first byte in range. + /// Offset of last byte in range. + private void CreateSimpleEntries(FormatDescriptor.Type type, + FormatDescriptor.SubType subType, int chunkLength, + WeakSymbolRef symbolRef, int low, int high) { + + if (chunkLength == -1) { + chunkLength = (high - low) + 1; + } + Debug.Assert(((high - low + 1) / chunkLength) * chunkLength == high - low + 1); + + // Either we have one chunk, or we have multiple chunks with the same type and + // length. Either way, we only need to create the descriptor once. (This is + // safe because FormatDescriptor instances are immutable.) + // + // Because certain details, like the fill byte and high-vs-low ASCII, are pulled + // out of the data stream at format time, we don't have to dig for them now. + FormatDescriptor dfd; + if (subType == FormatDescriptor.SubType.Symbol) { + dfd = FormatDescriptor.Create(chunkLength, symbolRef, + type == FormatDescriptor.Type.NumericBE); + } else { + dfd = FormatDescriptor.Create(chunkLength, type, subType); + } + + while (low <= high) { + Results.Add(low, dfd); + low += chunkLength; + } + } + + /// + /// Creates one or more FormatDescriptor entries for the specified range, adding them + /// to the Results list. + /// + /// Offset of first byte in range. + /// Offset of last byte in range. + /// String sub-type. + private void CreateMixedStringEntries(int low, int high, + FormatDescriptor.SubType subType) { + int stringStart = -1; + int highBit = 0; + int cur; + for (cur = low; cur <= high; cur++) { + byte val = mFileData[cur]; + if (CommonUtil.TextUtil.IsHiLoAscii(val)) { + // is ASCII + if (stringStart >= 0) { + // was in a string + if (highBit != (val & 0x80)) { + // end of string due to high bit flip, output + CreateStringOrByte(stringStart, cur - stringStart, subType); + // start a new string + stringStart = cur; + } else { + // still in string, keep going + } + } else { + // wasn't in a string, start one + stringStart = cur; + } + highBit = val & 0x80; + } else { + // not ASCII + if (stringStart >= 0) { + // was in a string, output it + CreateStringOrByte(stringStart, cur - stringStart, subType); + stringStart = -1; + } + // output as single byte + CreateByteFD(cur, FormatDescriptor.SubType.Hex); + } + } + if (stringStart >= 0) { + // close out the string + CreateStringOrByte(stringStart, cur - stringStart, subType); + } + } + + /// + /// Creates a format descriptor for ASCII data. If the data is only one byte long, + /// a single-byte ASCII char item is emitted instead. + /// + /// Offset of first byte. + /// Length of string. + /// String sub-type. + private void CreateStringOrByte(int offset, int length, + FormatDescriptor.SubType subType) { + Debug.Assert(length > 0); + if (length == 1) { + // single byte, output as single ASCII char rather than 1-byte string + CreateByteFD(offset, FormatDescriptor.SubType.Ascii); + } else { + FormatDescriptor dfd; + dfd = FormatDescriptor.Create(length, + FormatDescriptor.Type.String, subType); + Results.Add(offset, dfd); + } + } + + /// + /// Creates a format descriptor for a single-byte numeric value. + /// + /// File offset. + /// How to format the item. + private void CreateByteFD(int offset, FormatDescriptor.SubType subType) { + FormatDescriptor dfd = FormatDescriptor.Create(1, + FormatDescriptor.Type.NumericLE, subType); + Results.Add(offset, dfd); + } + + /// + /// Creates one or more FormatDescriptor entries for the specified range, adding them + /// to the Results list. + /// + /// Offset of first byte in range. + /// Offset of last byte in range. + /// String sub-type. + private void CreateCStringEntries(int low, int high, + FormatDescriptor.SubType subType) { + int startOffset = low; + for (int i = low; i <= high; i++) { + if (mFileData[i] == 0x00) { + // End of string. Zero-length strings are allowed. + FormatDescriptor dfd = FormatDescriptor.Create( + i - startOffset + 1, FormatDescriptor.Type.String, subType); + Results.Add(startOffset, dfd); + startOffset = i + 1; + } else { + // keep going + } + } + + // Earlier analysis guaranteed that the last byte in the buffer is 0x00. + Debug.Assert(startOffset == high + 1); + } + + /// + /// Creates one or more FormatDescriptor entries for the specified range, adding them + /// to the Results list. + /// + /// Offset of first byte in range. + /// Offset of last byte in range. + /// String sub-type. + private void CreateLengthStringEntries(int low, int high, + FormatDescriptor.SubType subType) { + int i; + for (i = low; i <= high;) { + int length = mFileData[i]; + if (subType == FormatDescriptor.SubType.L16String) { + length |= mFileData[i + 1] << 8; + length += 2; + } else { + length++; + } + // Zero-length strings are allowed. + FormatDescriptor dfd = FormatDescriptor.Create(length, + FormatDescriptor.Type.String, subType); + Results.Add(i, dfd); + i += length; + } + + Debug.Assert(i == high + 1); + } + + /// + /// Creates one or more FormatDescriptor entries for the specified range, adding them + /// to the Results list. + /// + /// Offset of first byte in range. + /// Offset of last byte in range. + /// String sub-type. + private void CreateDciStringEntries(int low, int high, + FormatDescriptor.SubType subType) { + int start, end, adj, endMask; + if (subType == FormatDescriptor.SubType.Dci) { + start = low; + end = high + 1; + adj = 1; + } else if (subType == FormatDescriptor.SubType.DciReverse) { + start = high; + end = low - 1; + adj = -1; + } else { + Debug.Assert(false); + return; + } + + // Zero-length strings aren't a thing for DCI. The analyzer requires that all + // strings in a region have the same polarity, so just grab the last byte. + endMask = mFileData[end - 1] & 0x80; + + int stringStart = start; + for (int i = start; i != end; i += adj) { + byte val = mFileData[i]; + if ((val & 0x80) == endMask) { + // found the end of a string + int length = (i - stringStart) * adj + 1; + FormatDescriptor dfd = FormatDescriptor.Create(length, + FormatDescriptor.Type.String, subType); + Results.Add(stringStart < i ? stringStart : i, dfd); + stringStart = i + adj; + } + } + + Debug.Assert(stringStart == end); + } + } +} diff --git a/SourceGen/AppForms/EditData.resx b/SourceGen/AppForms/EditData.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/EditData.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/EditDefSymbol.Designer.cs b/SourceGen/AppForms/EditDefSymbol.Designer.cs new file mode 100644 index 0000000..0f2aac9 --- /dev/null +++ b/SourceGen/AppForms/EditDefSymbol.Designer.cs @@ -0,0 +1,265 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class EditDefSymbol { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.cancelButton = new System.Windows.Forms.Button(); + this.okButton = new System.Windows.Forms.Button(); + this.labelLabel = new System.Windows.Forms.Label(); + this.labelTextBox = new System.Windows.Forms.TextBox(); + this.valueTextBox = new System.Windows.Forms.TextBox(); + this.labelNotesLabel = new System.Windows.Forms.Label(); + this.valueLabel = new System.Windows.Forms.Label(); + this.valueNotesLabel = new System.Windows.Forms.Label(); + this.commentLabel = new System.Windows.Forms.Label(); + this.commentTextBox = new System.Windows.Forms.TextBox(); + this.commentNotesLabel = new System.Windows.Forms.Label(); + this.symbolTypeGroupBox = new System.Windows.Forms.GroupBox(); + this.constantRadioButton = new System.Windows.Forms.RadioButton(); + this.addressRadioButton = new System.Windows.Forms.RadioButton(); + this.labelUniqueLabel = new System.Windows.Forms.Label(); + this.symbolTypeGroupBox.SuspendLayout(); + this.SuspendLayout(); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(203, 253); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 5; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(122, 253); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 4; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // labelLabel + // + this.labelLabel.AutoSize = true; + this.labelLabel.Location = new System.Drawing.Point(14, 16); + this.labelLabel.Name = "labelLabel"; + this.labelLabel.Size = new System.Drawing.Size(36, 13); + this.labelLabel.TabIndex = 6; + this.labelLabel.Text = "Label:"; + // + // labelTextBox + // + this.labelTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.labelTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.labelTextBox.Location = new System.Drawing.Point(77, 14); + this.labelTextBox.Name = "labelTextBox"; + this.labelTextBox.Size = new System.Drawing.Size(200, 20); + this.labelTextBox.TabIndex = 0; + this.labelTextBox.TextChanged += new System.EventHandler(this.labelTextBox_TextChanged); + // + // valueTextBox + // + this.valueTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.valueTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.valueTextBox.Location = new System.Drawing.Point(77, 81); + this.valueTextBox.Name = "valueTextBox"; + this.valueTextBox.Size = new System.Drawing.Size(200, 20); + this.valueTextBox.TabIndex = 1; + this.valueTextBox.TextChanged += new System.EventHandler(this.valueTextBox_TextChanged); + // + // labelNotesLabel + // + this.labelNotesLabel.AutoSize = true; + this.labelNotesLabel.Location = new System.Drawing.Point(74, 37); + this.labelNotesLabel.Name = "labelNotesLabel"; + this.labelNotesLabel.Size = new System.Drawing.Size(187, 13); + this.labelNotesLabel.TabIndex = 9; + this.labelNotesLabel.Text = "• 2+ alphanumerics, starting with letter"; + // + // valueLabel + // + this.valueLabel.AutoSize = true; + this.valueLabel.Location = new System.Drawing.Point(14, 83); + this.valueLabel.Name = "valueLabel"; + this.valueLabel.Size = new System.Drawing.Size(37, 13); + this.valueLabel.TabIndex = 7; + this.valueLabel.Text = "Value:"; + // + // valueNotesLabel + // + this.valueNotesLabel.AutoSize = true; + this.valueNotesLabel.Location = new System.Drawing.Point(74, 104); + this.valueNotesLabel.Name = "valueNotesLabel"; + this.valueNotesLabel.Size = new System.Drawing.Size(155, 13); + this.valueNotesLabel.TabIndex = 11; + this.valueNotesLabel.Text = "• Decimal, hex ($), or binary (%)"; + // + // commentLabel + // + this.commentLabel.AutoSize = true; + this.commentLabel.Location = new System.Drawing.Point(17, 137); + this.commentLabel.Name = "commentLabel"; + this.commentLabel.Size = new System.Drawing.Size(54, 13); + this.commentLabel.TabIndex = 8; + this.commentLabel.Text = "Comment:"; + // + // commentTextBox + // + this.commentTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.commentTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.commentTextBox.Location = new System.Drawing.Point(77, 134); + this.commentTextBox.Name = "commentTextBox"; + this.commentTextBox.Size = new System.Drawing.Size(200, 20); + this.commentTextBox.TabIndex = 2; + // + // commentNotesLabel + // + this.commentNotesLabel.AutoSize = true; + this.commentNotesLabel.Location = new System.Drawing.Point(74, 157); + this.commentNotesLabel.Name = "commentNotesLabel"; + this.commentNotesLabel.Size = new System.Drawing.Size(55, 13); + this.commentNotesLabel.TabIndex = 12; + this.commentNotesLabel.Text = "• Optional"; + // + // symbolTypeGroupBox + // + this.symbolTypeGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.symbolTypeGroupBox.Controls.Add(this.constantRadioButton); + this.symbolTypeGroupBox.Controls.Add(this.addressRadioButton); + this.symbolTypeGroupBox.Location = new System.Drawing.Point(13, 190); + this.symbolTypeGroupBox.Name = "symbolTypeGroupBox"; + this.symbolTypeGroupBox.Size = new System.Drawing.Size(264, 48); + this.symbolTypeGroupBox.TabIndex = 3; + this.symbolTypeGroupBox.TabStop = false; + this.symbolTypeGroupBox.Text = "Symbol Type"; + // + // constantRadioButton + // + this.constantRadioButton.AutoSize = true; + this.constantRadioButton.Location = new System.Drawing.Point(99, 20); + this.constantRadioButton.Name = "constantRadioButton"; + this.constantRadioButton.Size = new System.Drawing.Size(67, 17); + this.constantRadioButton.TabIndex = 1; + this.constantRadioButton.TabStop = true; + this.constantRadioButton.Text = "Constant"; + this.constantRadioButton.UseVisualStyleBackColor = true; + // + // addressRadioButton + // + this.addressRadioButton.AutoSize = true; + this.addressRadioButton.Location = new System.Drawing.Point(6, 20); + this.addressRadioButton.Name = "addressRadioButton"; + this.addressRadioButton.Size = new System.Drawing.Size(63, 17); + this.addressRadioButton.TabIndex = 0; + this.addressRadioButton.TabStop = true; + this.addressRadioButton.Text = "Address"; + this.addressRadioButton.UseVisualStyleBackColor = true; + // + // labelUniqueLabel + // + this.labelUniqueLabel.AutoSize = true; + this.labelUniqueLabel.Location = new System.Drawing.Point(74, 53); + this.labelUniqueLabel.Name = "labelUniqueLabel"; + this.labelUniqueLabel.Size = new System.Drawing.Size(160, 13); + this.labelUniqueLabel.TabIndex = 10; + this.labelUniqueLabel.Text = "• Unique among project symbols"; + // + // EditDefSymbol + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(290, 288); + this.Controls.Add(this.labelUniqueLabel); + this.Controls.Add(this.symbolTypeGroupBox); + this.Controls.Add(this.commentNotesLabel); + this.Controls.Add(this.commentTextBox); + this.Controls.Add(this.commentLabel); + this.Controls.Add(this.valueNotesLabel); + this.Controls.Add(this.valueLabel); + this.Controls.Add(this.labelNotesLabel); + this.Controls.Add(this.valueTextBox); + this.Controls.Add(this.labelTextBox); + this.Controls.Add(this.labelLabel); + this.Controls.Add(this.okButton); + this.Controls.Add(this.cancelButton); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditDefSymbol"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Symbol"; + this.Load += new System.EventHandler(this.EditDefSymbol_Load); + this.symbolTypeGroupBox.ResumeLayout(false); + this.symbolTypeGroupBox.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Label labelLabel; + private System.Windows.Forms.TextBox labelTextBox; + private System.Windows.Forms.TextBox valueTextBox; + private System.Windows.Forms.Label labelNotesLabel; + private System.Windows.Forms.Label valueLabel; + private System.Windows.Forms.Label valueNotesLabel; + private System.Windows.Forms.Label commentLabel; + private System.Windows.Forms.TextBox commentTextBox; + private System.Windows.Forms.Label commentNotesLabel; + private System.Windows.Forms.GroupBox symbolTypeGroupBox; + private System.Windows.Forms.RadioButton constantRadioButton; + private System.Windows.Forms.RadioButton addressRadioButton; + private System.Windows.Forms.Label labelUniqueLabel; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/EditDefSymbol.cs b/SourceGen/AppForms/EditDefSymbol.cs new file mode 100644 index 0000000..5fafa68 --- /dev/null +++ b/SourceGen/AppForms/EditDefSymbol.cs @@ -0,0 +1,132 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Drawing; +using System.Windows.Forms; + +using Asm65; + +namespace SourceGen.AppForms { + public partial class EditDefSymbol : Form { + /// + /// Set to previous value before calling; may be null if creating a new symbol. + /// Will be set to new value on OK result. + /// + public DefSymbol DefSym { get; set; } + + /// + /// Format object to use when formatting addresses and constants. + /// + private Formatter NumFormatter { get; set; } + + /// + /// List of existing symbols, for uniqueness check. The list will not be modified. + /// + private SortedList DefSymbolList { get; set; } + + // Saved off at dialog load time. + private Color mDefaultLabelColor; + + + public EditDefSymbol(Formatter formatter, SortedList defList) { + InitializeComponent(); + + NumFormatter = formatter; + DefSymbolList = defList; + } + + private void EditDefSymbol_Load(object sender, EventArgs e) { + mDefaultLabelColor = labelNotesLabel.ForeColor; + + if (DefSym != null) { + labelTextBox.Text = DefSym.Label; + valueTextBox.Text = NumFormatter.FormatValueInBase(DefSym.Value, + DefSym.DataDescriptor.NumBase); + commentTextBox.Text = DefSym.Comment; + + if (DefSym.SymbolType == Symbol.Type.Constant) { + constantRadioButton.Checked = true; + } else { + addressRadioButton.Checked = true; + } + } else { + addressRadioButton.Checked = true; + } + + UpdateControls(); + } + + private void UpdateControls() { + bool labelValid, labelUnique, valueValid; + + // Label must be valid and not already exist in project symbol list. (It's okay + // if it exists elsewhere.) + labelValid = Asm65.Label.ValidateLabel(labelTextBox.Text); + + if (DefSymbolList.TryGetValue(labelTextBox.Text, out DefSymbol existing)) { + // It's okay if it's the same object. + labelUnique = (existing == DefSym); + } else { + labelUnique = true; + } + + // Value must be blank, meaning "erase any earlier definition", or valid value. + // (Hmm... don't currently have a way to specify "no symbol" in DefSymbol.) + //if (!string.IsNullOrEmpty(valueTextBox.Text)) { + valueValid = ParseValue(out int unused1, out int unused2); + //} else { + // valueValid = true; + //} + + labelNotesLabel.ForeColor = labelValid ? mDefaultLabelColor : Color.Red; + labelUniqueLabel.ForeColor = labelUnique ? mDefaultLabelColor : Color.Red; + valueNotesLabel.ForeColor = valueValid ? mDefaultLabelColor : Color.Red; + + okButton.Enabled = labelValid && labelUnique && valueValid; + } + + private bool ParseValue(out int value, out int numBase) { + string str = valueTextBox.Text; + if (str.IndexOf('/') >= 0) { + // treat as address + numBase = 16; + return Asm65.Address.ParseAddress(str, (1 << 24) - 1, out value); + } else { + return Asm65.Number.TryParseInt(str, out value, out numBase); + } + } + + private void okButton_Click(object sender, EventArgs e) { + bool isConstant = constantRadioButton.Checked; + + ParseValue(out int value, out int numBase); + FormatDescriptor.SubType subType = FormatDescriptor.GetSubTypeForBase(numBase); + DefSym = new DefSymbol(labelTextBox.Text, value, Symbol.Source.Project, + isConstant ? Symbol.Type.Constant : Symbol.Type.ExternalAddr, + subType, commentTextBox.Text, string.Empty); + } + + private void labelTextBox_TextChanged(object sender, EventArgs e) { + UpdateControls(); + } + + private void valueTextBox_TextChanged(object sender, EventArgs e) { + UpdateControls(); + } + } +} diff --git a/SourceGen/AppForms/EditDefSymbol.resx b/SourceGen/AppForms/EditDefSymbol.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/EditDefSymbol.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/EditLabel.Designer.cs b/SourceGen/AppForms/EditLabel.Designer.cs new file mode 100644 index 0000000..f1ae745 --- /dev/null +++ b/SourceGen/AppForms/EditLabel.Designer.cs @@ -0,0 +1,225 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class EditLabel { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.cancelButton = new System.Windows.Forms.Button(); + this.okButton = new System.Windows.Forms.Button(); + this.instructionLabel = new System.Windows.Forms.Label(); + this.labelTextBox = new System.Windows.Forms.TextBox(); + this.maxLengthLabel = new System.Windows.Forms.Label(); + this.firstLetterLabel = new System.Windows.Forms.Label(); + this.validCharsLabel = new System.Windows.Forms.Label(); + this.notDuplicateLabel = new System.Windows.Forms.Label(); + this.labelTypeGroupBox = new System.Windows.Forms.GroupBox(); + this.radioButtonExport = new System.Windows.Forms.RadioButton(); + this.radioButtonGlobal = new System.Windows.Forms.RadioButton(); + this.radioButtonLocal = new System.Windows.Forms.RadioButton(); + this.labelTypeGroupBox.SuspendLayout(); + this.SuspendLayout(); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(267, 202); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 8; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(186, 202); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 7; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // instructionLabel + // + this.instructionLabel.AutoSize = true; + this.instructionLabel.Location = new System.Drawing.Point(13, 13); + this.instructionLabel.Name = "instructionLabel"; + this.instructionLabel.Size = new System.Drawing.Size(60, 13); + this.instructionLabel.TabIndex = 0; + this.instructionLabel.Text = "Enter label:"; + // + // labelTextBox + // + this.labelTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.labelTextBox.Location = new System.Drawing.Point(13, 30); + this.labelTextBox.Name = "labelTextBox"; + this.labelTextBox.Size = new System.Drawing.Size(325, 20); + this.labelTextBox.TabIndex = 1; + this.labelTextBox.TextChanged += new System.EventHandler(this.labelTextBox_TextChanged); + // + // maxLengthLabel + // + this.maxLengthLabel.AutoSize = true; + this.maxLengthLabel.Location = new System.Drawing.Point(13, 57); + this.maxLengthLabel.Name = "maxLengthLabel"; + this.maxLengthLabel.Size = new System.Drawing.Size(251, 13); + this.maxLengthLabel.TabIndex = 2; + this.maxLengthLabel.Text = "• Must be 2-32 characters long (or blank to remove)"; + // + // firstLetterLabel + // + this.firstLetterLabel.AutoSize = true; + this.firstLetterLabel.Location = new System.Drawing.Point(12, 73); + this.firstLetterLabel.Name = "firstLetterLabel"; + this.firstLetterLabel.Size = new System.Drawing.Size(187, 13); + this.firstLetterLabel.TabIndex = 3; + this.firstLetterLabel.Text = "• Must start with a letter or underscore"; + // + // validCharsLabel + // + this.validCharsLabel.AutoSize = true; + this.validCharsLabel.Location = new System.Drawing.Point(12, 89); + this.validCharsLabel.Name = "validCharsLabel"; + this.validCharsLabel.Size = new System.Drawing.Size(297, 13); + this.validCharsLabel.TabIndex = 4; + this.validCharsLabel.Text = "• Valid characters are ASCII letters, numbers, and underscore"; + // + // notDuplicateLabel + // + this.notDuplicateLabel.AutoSize = true; + this.notDuplicateLabel.Location = new System.Drawing.Point(13, 105); + this.notDuplicateLabel.Name = "notDuplicateLabel"; + this.notDuplicateLabel.Size = new System.Drawing.Size(217, 13); + this.notDuplicateLabel.TabIndex = 5; + this.notDuplicateLabel.Text = "• Must not be a duplicate of an existing label"; + // + // labelTypeGroupBox + // + this.labelTypeGroupBox.Controls.Add(this.radioButtonExport); + this.labelTypeGroupBox.Controls.Add(this.radioButtonGlobal); + this.labelTypeGroupBox.Controls.Add(this.radioButtonLocal); + this.labelTypeGroupBox.Location = new System.Drawing.Point(13, 132); + this.labelTypeGroupBox.Name = "labelTypeGroupBox"; + this.labelTypeGroupBox.Size = new System.Drawing.Size(137, 93); + this.labelTypeGroupBox.TabIndex = 6; + this.labelTypeGroupBox.TabStop = false; + this.labelTypeGroupBox.Text = "Label Type"; + // + // radioButtonExport + // + this.radioButtonExport.AutoSize = true; + this.radioButtonExport.Location = new System.Drawing.Point(7, 67); + this.radioButtonExport.Name = "radioButtonExport"; + this.radioButtonExport.Size = new System.Drawing.Size(120, 17); + this.radioButtonExport.TabIndex = 2; + this.radioButtonExport.TabStop = true; + this.radioButtonExport.Text = "Global and &exported"; + this.radioButtonExport.UseVisualStyleBackColor = true; + // + // radioButtonGlobal + // + this.radioButtonGlobal.AutoSize = true; + this.radioButtonGlobal.Location = new System.Drawing.Point(7, 43); + this.radioButtonGlobal.Name = "radioButtonGlobal"; + this.radioButtonGlobal.Size = new System.Drawing.Size(55, 17); + this.radioButtonGlobal.TabIndex = 1; + this.radioButtonGlobal.TabStop = true; + this.radioButtonGlobal.Text = "&Global"; + this.radioButtonGlobal.UseVisualStyleBackColor = true; + // + // radioButtonLocal + // + this.radioButtonLocal.AutoSize = true; + this.radioButtonLocal.Location = new System.Drawing.Point(7, 20); + this.radioButtonLocal.Name = "radioButtonLocal"; + this.radioButtonLocal.Size = new System.Drawing.Size(106, 17); + this.radioButtonLocal.TabIndex = 0; + this.radioButtonLocal.TabStop = true; + this.radioButtonLocal.Text = "&Local (if possible)"; + this.radioButtonLocal.UseVisualStyleBackColor = true; + // + // EditLabel + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(354, 237); + this.Controls.Add(this.labelTypeGroupBox); + this.Controls.Add(this.notDuplicateLabel); + this.Controls.Add(this.validCharsLabel); + this.Controls.Add(this.firstLetterLabel); + this.Controls.Add(this.maxLengthLabel); + this.Controls.Add(this.labelTextBox); + this.Controls.Add(this.instructionLabel); + this.Controls.Add(this.okButton); + this.Controls.Add(this.cancelButton); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditLabel"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Label"; + this.Load += new System.EventHandler(this.EditLabel_Load); + this.labelTypeGroupBox.ResumeLayout(false); + this.labelTypeGroupBox.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Label instructionLabel; + private System.Windows.Forms.TextBox labelTextBox; + private System.Windows.Forms.Label maxLengthLabel; + private System.Windows.Forms.Label firstLetterLabel; + private System.Windows.Forms.Label validCharsLabel; + private System.Windows.Forms.Label notDuplicateLabel; + private System.Windows.Forms.GroupBox labelTypeGroupBox; + private System.Windows.Forms.RadioButton radioButtonLocal; + private System.Windows.Forms.RadioButton radioButtonGlobal; + private System.Windows.Forms.RadioButton radioButtonExport; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/EditLabel.cs b/SourceGen/AppForms/EditLabel.cs new file mode 100644 index 0000000..cc7070c --- /dev/null +++ b/SourceGen/AppForms/EditLabel.cs @@ -0,0 +1,147 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Drawing; +using System.Windows.Forms; + +namespace SourceGen.AppForms { + public partial class EditLabel : Form { + /// + /// Symbol object. May initially be null. When the dialog completes successfully, + /// this will have the new symbol, or null if the user deleted the label. + /// + public Symbol LabelSym { get; set; } + + /// + /// Address we are editing the label for. + /// + public int Address { private get; set; } + + // Saved off at dialog load time. + private Color mDefaultLabelColor; + + // Reference to DisasmProject's SymbolTable. + private SymbolTable mSymbolTable; + + + public EditLabel(SymbolTable symbolTable) { + InitializeComponent(); + mSymbolTable = symbolTable; + } + + private void EditLabel_Load(object sender, EventArgs e) { + mDefaultLabelColor = maxLengthLabel.ForeColor; + + if (LabelSym == null) { + labelTextBox.Text = string.Empty; + radioButtonLocal.Checked = true; + } else { + labelTextBox.Text = LabelSym.Label; + switch (LabelSym.SymbolType) { + case Symbol.Type.LocalOrGlobalAddr: + radioButtonLocal.Checked = true; + break; + case Symbol.Type.GlobalAddr: + radioButtonGlobal.Checked = true; + break; + case Symbol.Type.GlobalAddrExport: + radioButtonExport.Checked = true; + break; + default: + Debug.Assert(false); // WTF + radioButtonLocal.Checked = true; + break; + } + } + } + + private void labelTextBox_TextChanged(object sender, EventArgs e) { + string str = labelTextBox.Text; + bool valid = true; + + if (str.Length == 1 || str.Length > Asm65.Label.MAX_LABEL_LEN) { + valid = false; + maxLengthLabel.ForeColor = Color.Red; + } else { + maxLengthLabel.ForeColor = mDefaultLabelColor; + } + + // Regex never matches on strings of length 0 or 1, but we don't want + // to complain about that since we're already doing that above. + // TODO(maybe): Ideally this wouldn't light up if the only problem was a + // non-alpha first character, since the next test will call that out. + if (str.Length > 1) { + if (!Asm65.Label.ValidateLabel(str)) { + valid = false; + validCharsLabel.ForeColor = Color.Red; + } else { + validCharsLabel.ForeColor = mDefaultLabelColor; + } + } else { + validCharsLabel.ForeColor = mDefaultLabelColor; + } + + if (str.Length > 0 && + !((str[0] >= 'A' && str[0] <= 'Z') || (str[0] >= 'a' && str[0] <= 'z') || + str[0] == '_')) { + // This should have been caught by the regex. We just want to set the + // color on the "first character must be" instruction text. + Debug.Assert(!valid); + firstLetterLabel.ForeColor = Color.Red; + } else { + firstLetterLabel.ForeColor = mDefaultLabelColor; + } + + // Refuse to continue if the label already exists. The only exception is if + // it's the same symbol, and it's user-defined. (If they're trying to edit an + // auto label, we want to force them to change the name.) + // + // NOTE: if label matching is case-insensitive, we want to allow a situation + // where a label is being renamed from "FOO" to "Foo". We should be able to + // test for object equality on the Symbol to determine if we're renaming a + // symbol to itself. + if (valid && mSymbolTable.TryGetValue(str, out Symbol sym) && + (sym != LabelSym || LabelSym.SymbolSource != Symbol.Source.User)) { + valid = false; + notDuplicateLabel.ForeColor = Color.Red; + } else { + notDuplicateLabel.ForeColor = mDefaultLabelColor; + } + + okButton.Enabled = valid; + } + + private void okButton_Click(object sender, EventArgs e) { + if (string.IsNullOrEmpty(labelTextBox.Text)) { + LabelSym = null; + } else { + Symbol.Type symbolType; + if (radioButtonLocal.Checked) { + symbolType = Symbol.Type.LocalOrGlobalAddr; + } else if (radioButtonGlobal.Checked) { + symbolType = Symbol.Type.GlobalAddr; + } else if (radioButtonExport.Checked) { + symbolType = Symbol.Type.GlobalAddrExport; + } else { + Debug.Assert(false); // WTF + symbolType = Symbol.Type.LocalOrGlobalAddr; + } + LabelSym = new Symbol(labelTextBox.Text, Address, Symbol.Source.User, symbolType); + } + } + } +} diff --git a/SourceGen/AppForms/EditLabel.resx b/SourceGen/AppForms/EditLabel.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/EditLabel.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/EditLongComment.Designer.cs b/SourceGen/AppForms/EditLongComment.Designer.cs new file mode 100644 index 0000000..0c8b331 --- /dev/null +++ b/SourceGen/AppForms/EditLongComment.Designer.cs @@ -0,0 +1,201 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class EditLongComment { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.entryTextBox = new System.Windows.Forms.TextBox(); + this.textEntryLabel = new System.Windows.Forms.Label(); + this.sampleOutputLabel = new System.Windows.Forms.Label(); + this.displayTextBox = new System.Windows.Forms.TextBox(); + this.okButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.maximumWidthLabel = new System.Windows.Forms.Label(); + this.maxWidthComboBox = new System.Windows.Forms.ComboBox(); + this.boxModeCheckBox = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // entryTextBox + // + this.entryTextBox.AcceptsReturn = true; + this.entryTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.entryTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.entryTextBox.Location = new System.Drawing.Point(12, 29); + this.entryTextBox.Multiline = true; + this.entryTextBox.Name = "entryTextBox"; + this.entryTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.entryTextBox.Size = new System.Drawing.Size(509, 150); + this.entryTextBox.TabIndex = 0; + this.entryTextBox.Text = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"; + this.entryTextBox.TextChanged += new System.EventHandler(this.entryTextBox_TextChanged); + // + // textEntryLabel + // + this.textEntryLabel.AutoSize = true; + this.textEntryLabel.Location = new System.Drawing.Point(13, 12); + this.textEntryLabel.Name = "textEntryLabel"; + this.textEntryLabel.Size = new System.Drawing.Size(101, 13); + this.textEntryLabel.TabIndex = 1; + this.textEntryLabel.Text = "Enter comment text:"; + // + // sampleOutputLabel + // + this.sampleOutputLabel.AutoSize = true; + this.sampleOutputLabel.Location = new System.Drawing.Point(12, 240); + this.sampleOutputLabel.Name = "sampleOutputLabel"; + this.sampleOutputLabel.Size = new System.Drawing.Size(88, 13); + this.sampleOutputLabel.TabIndex = 5; + this.sampleOutputLabel.Text = "Expected output:"; + // + // displayTextBox + // + this.displayTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.displayTextBox.BackColor = System.Drawing.SystemColors.Window; + this.displayTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.displayTextBox.Location = new System.Drawing.Point(12, 258); + this.displayTextBox.Multiline = true; + this.displayTextBox.Name = "displayTextBox"; + this.displayTextBox.ReadOnly = true; + this.displayTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.displayTextBox.Size = new System.Drawing.Size(509, 163); + this.displayTextBox.TabIndex = 6; + this.displayTextBox.Text = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(365, 438); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 7; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(446, 438); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 8; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // maximumWidthLabel + // + this.maximumWidthLabel.AutoSize = true; + this.maximumWidthLabel.Location = new System.Drawing.Point(12, 198); + this.maximumWidthLabel.Name = "maximumWidthLabel"; + this.maximumWidthLabel.Size = new System.Drawing.Size(101, 13); + this.maximumWidthLabel.TabIndex = 2; + this.maximumWidthLabel.Text = "Maximum line width:"; + // + // maxWidthComboBox + // + this.maxWidthComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.maxWidthComboBox.FormattingEnabled = true; + this.maxWidthComboBox.Items.AddRange(new object[] { + "30", + "40", + "64", + "80"}); + this.maxWidthComboBox.Location = new System.Drawing.Point(119, 195); + this.maxWidthComboBox.Name = "maxWidthComboBox"; + this.maxWidthComboBox.Size = new System.Drawing.Size(74, 21); + this.maxWidthComboBox.TabIndex = 3; + this.maxWidthComboBox.SelectedIndexChanged += new System.EventHandler(this.maxWidthComboBox_SelectedIndexChanged); + // + // boxModeCheckBox + // + this.boxModeCheckBox.AutoSize = true; + this.boxModeCheckBox.Location = new System.Drawing.Point(264, 197); + this.boxModeCheckBox.Name = "boxModeCheckBox"; + this.boxModeCheckBox.Size = new System.Drawing.Size(92, 17); + this.boxModeCheckBox.TabIndex = 4; + this.boxModeCheckBox.Text = "Render in box"; + this.boxModeCheckBox.UseVisualStyleBackColor = true; + this.boxModeCheckBox.CheckedChanged += new System.EventHandler(this.boxModeCheckBox_CheckedChanged); + // + // EditLongComment + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(534, 473); + this.Controls.Add(this.boxModeCheckBox); + this.Controls.Add(this.maxWidthComboBox); + this.Controls.Add(this.maximumWidthLabel); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.okButton); + this.Controls.Add(this.displayTextBox); + this.Controls.Add(this.sampleOutputLabel); + this.Controls.Add(this.textEntryLabel); + this.Controls.Add(this.entryTextBox); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(550, 512); + this.Name = "EditLongComment"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Long Comment"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.EditLongComment_HelpButtonClicked); + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.EditLongComment_FormClosing); + this.Load += new System.EventHandler(this.EditLongComment_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox entryTextBox; + private System.Windows.Forms.Label textEntryLabel; + private System.Windows.Forms.Label sampleOutputLabel; + private System.Windows.Forms.TextBox displayTextBox; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Label maximumWidthLabel; + private System.Windows.Forms.ComboBox maxWidthComboBox; + private System.Windows.Forms.CheckBox boxModeCheckBox; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/EditLongComment.cs b/SourceGen/AppForms/EditLongComment.cs new file mode 100644 index 0000000..fad944f --- /dev/null +++ b/SourceGen/AppForms/EditLongComment.cs @@ -0,0 +1,130 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace SourceGen.AppForms { + public partial class EditLongComment : Form { + /// + /// Get or set the multi-line comment object. On exit, will be set to null if + /// the user wants to delete the comment. + /// + public MultiLineComment LongComment { get; set; } + + private Asm65.Formatter mFormatter; + + + public EditLongComment(Asm65.Formatter formatter) { + InitializeComponent(); + + mFormatter = formatter; + LongComment = new MultiLineComment(string.Empty); + } + + private void EditLongComment_Load(object sender, EventArgs e) { + Debug.Assert(LongComment != null); + entryTextBox.Text = LongComment.Text; + boxModeCheckBox.Checked = LongComment.BoxMode; + + maxWidthComboBox.SelectedIndex = 0; + for (int i = 0; i < maxWidthComboBox.Items.Count; i++) { + string item = (string) maxWidthComboBox.Items[i]; + if (int.Parse(item) == LongComment.MaxWidth) { + maxWidthComboBox.SelectedIndex = i; + break; + } + } + + FormatInput(); + } + + // Handle Ctrl+Enter. + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { + if (keyData == (Keys.Control | Keys.Enter)) { + DialogResult = DialogResult.OK; + Close(); + return true; + } + return base.ProcessCmdKey(ref msg, keyData); + } + + private void EditLongComment_FormClosing(object sender, FormClosingEventArgs e) { + if (string.IsNullOrEmpty(entryTextBox.Text)) { + LongComment = null; + } else { + LongComment = CreateMLC(); + } + } + + private void entryTextBox_TextChanged(object sender, EventArgs e) { + FormatInput(); + } + + private void maxWidthComboBox_SelectedIndexChanged(object sender, EventArgs e) { + FormatInput(); + } + + private void boxModeCheckBox_CheckedChanged(object sender, EventArgs e) { + FormatInput(); + } + + /// + /// Creates a MultiLineComment from the current state of the dialog. + /// + /// New MultiLineComment object. Returns null if the dialog is still + /// in the process of initializing. + private MultiLineComment CreateMLC() { + if (maxWidthComboBox.SelectedItem == null) { + return null; // still initializing + } + return new MultiLineComment(entryTextBox.Text, boxModeCheckBox.Checked, + int.Parse((string)maxWidthComboBox.SelectedItem)); + } + + /// + /// Formats entryTextBox.Text into displayTextBox.Text. + /// + private void FormatInput() { + MultiLineComment mlc = CreateMLC(); + if (mlc == null) { + return; + } + List lines = mlc.FormatText(mFormatter, string.Empty); + + StringBuilder sb = new StringBuilder(entryTextBox.Text.Length + lines.Count * 2); + //sb.AppendFormat("### got {0} lines\r\n", lines.Count); + bool first = true; + foreach (string line in lines) { + if (first) { + first = false; + } else { + sb.Append("\r\n"); + } + sb.Append(line); + } + + displayTextBox.Text = sb.ToString(); + } + + private void EditLongComment_HelpButtonClicked(object sender, System.ComponentModel.CancelEventArgs e) { + HelpAccess.ShowHelp(HelpAccess.Topic.EditLongComment); + } + } +} diff --git a/SourceGen/AppForms/EditLongComment.resx b/SourceGen/AppForms/EditLongComment.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/EditLongComment.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/EditNote.Designer.cs b/SourceGen/AppForms/EditNote.Designer.cs new file mode 100644 index 0000000..2b52009 --- /dev/null +++ b/SourceGen/AppForms/EditNote.Designer.cs @@ -0,0 +1,221 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class EditNote { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.cancelButton = new System.Windows.Forms.Button(); + this.okButton = new System.Windows.Forms.Button(); + this.headerLabel = new System.Windows.Forms.Label(); + this.noteTextBox = new System.Windows.Forms.TextBox(); + this.colorGroupBox = new System.Windows.Forms.GroupBox(); + this.colorOrangeRadio = new System.Windows.Forms.RadioButton(); + this.colorPinkRadio = new System.Windows.Forms.RadioButton(); + this.colorYellowRadio = new System.Windows.Forms.RadioButton(); + this.colorBlueRadio = new System.Windows.Forms.RadioButton(); + this.colorGreenRadio = new System.Windows.Forms.RadioButton(); + this.colorDefaultRadio = new System.Windows.Forms.RadioButton(); + this.colorGroupBox.SuspendLayout(); + this.SuspendLayout(); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(552, 225); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 3; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(471, 225); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 2; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + // + // headerLabel + // + this.headerLabel.AutoSize = true; + this.headerLabel.Location = new System.Drawing.Point(13, 13); + this.headerLabel.Name = "headerLabel"; + this.headerLabel.Size = new System.Drawing.Size(59, 13); + this.headerLabel.TabIndex = 0; + this.headerLabel.Text = "Enter note:"; + // + // noteTextBox + // + this.noteTextBox.AcceptsReturn = true; + this.noteTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.noteTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.noteTextBox.Location = new System.Drawing.Point(13, 33); + this.noteTextBox.Multiline = true; + this.noteTextBox.Name = "noteTextBox"; + this.noteTextBox.Size = new System.Drawing.Size(614, 113); + this.noteTextBox.TabIndex = 1; + // + // colorGroupBox + // + this.colorGroupBox.Controls.Add(this.colorOrangeRadio); + this.colorGroupBox.Controls.Add(this.colorPinkRadio); + this.colorGroupBox.Controls.Add(this.colorYellowRadio); + this.colorGroupBox.Controls.Add(this.colorBlueRadio); + this.colorGroupBox.Controls.Add(this.colorGreenRadio); + this.colorGroupBox.Controls.Add(this.colorDefaultRadio); + this.colorGroupBox.Location = new System.Drawing.Point(13, 153); + this.colorGroupBox.Name = "colorGroupBox"; + this.colorGroupBox.Size = new System.Drawing.Size(182, 95); + this.colorGroupBox.TabIndex = 4; + this.colorGroupBox.TabStop = false; + this.colorGroupBox.Text = "Select Highlight Color"; + // + // colorOrangeRadio + // + this.colorOrangeRadio.AutoSize = true; + this.colorOrangeRadio.Location = new System.Drawing.Point(98, 68); + this.colorOrangeRadio.Name = "colorOrangeRadio"; + this.colorOrangeRadio.Size = new System.Drawing.Size(60, 17); + this.colorOrangeRadio.TabIndex = 5; + this.colorOrangeRadio.TabStop = true; + this.colorOrangeRadio.Text = "&Orange"; + this.colorOrangeRadio.UseVisualStyleBackColor = true; + // + // colorPinkRadio + // + this.colorPinkRadio.AutoSize = true; + this.colorPinkRadio.Location = new System.Drawing.Point(98, 44); + this.colorPinkRadio.Name = "colorPinkRadio"; + this.colorPinkRadio.Size = new System.Drawing.Size(46, 17); + this.colorPinkRadio.TabIndex = 4; + this.colorPinkRadio.TabStop = true; + this.colorPinkRadio.Text = "&Pink"; + this.colorPinkRadio.UseVisualStyleBackColor = true; + // + // colorYellowRadio + // + this.colorYellowRadio.AutoSize = true; + this.colorYellowRadio.Location = new System.Drawing.Point(98, 20); + this.colorYellowRadio.Name = "colorYellowRadio"; + this.colorYellowRadio.Size = new System.Drawing.Size(56, 17); + this.colorYellowRadio.TabIndex = 3; + this.colorYellowRadio.TabStop = true; + this.colorYellowRadio.Text = "&Yellow"; + this.colorYellowRadio.UseVisualStyleBackColor = true; + // + // colorBlueRadio + // + this.colorBlueRadio.AutoSize = true; + this.colorBlueRadio.Location = new System.Drawing.Point(7, 68); + this.colorBlueRadio.Name = "colorBlueRadio"; + this.colorBlueRadio.Size = new System.Drawing.Size(46, 17); + this.colorBlueRadio.TabIndex = 2; + this.colorBlueRadio.TabStop = true; + this.colorBlueRadio.Text = "&Blue"; + this.colorBlueRadio.UseVisualStyleBackColor = true; + // + // colorGreenRadio + // + this.colorGreenRadio.AutoSize = true; + this.colorGreenRadio.Location = new System.Drawing.Point(7, 44); + this.colorGreenRadio.Name = "colorGreenRadio"; + this.colorGreenRadio.Size = new System.Drawing.Size(54, 17); + this.colorGreenRadio.TabIndex = 1; + this.colorGreenRadio.TabStop = true; + this.colorGreenRadio.Text = "&Green"; + this.colorGreenRadio.UseVisualStyleBackColor = true; + // + // colorDefaultRadio + // + this.colorDefaultRadio.AutoSize = true; + this.colorDefaultRadio.Location = new System.Drawing.Point(7, 20); + this.colorDefaultRadio.Name = "colorDefaultRadio"; + this.colorDefaultRadio.Size = new System.Drawing.Size(51, 17); + this.colorDefaultRadio.TabIndex = 0; + this.colorDefaultRadio.TabStop = true; + this.colorDefaultRadio.Text = "&None"; + this.colorDefaultRadio.UseVisualStyleBackColor = true; + // + // EditNote + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(639, 260); + this.Controls.Add(this.colorGroupBox); + this.Controls.Add(this.noteTextBox); + this.Controls.Add(this.headerLabel); + this.Controls.Add(this.okButton); + this.Controls.Add(this.cancelButton); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditNote"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Note"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.EditNote_FormClosing); + this.Load += new System.EventHandler(this.EditNote_Load); + this.colorGroupBox.ResumeLayout(false); + this.colorGroupBox.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Label headerLabel; + private System.Windows.Forms.TextBox noteTextBox; + private System.Windows.Forms.GroupBox colorGroupBox; + private System.Windows.Forms.RadioButton colorOrangeRadio; + private System.Windows.Forms.RadioButton colorPinkRadio; + private System.Windows.Forms.RadioButton colorYellowRadio; + private System.Windows.Forms.RadioButton colorBlueRadio; + private System.Windows.Forms.RadioButton colorGreenRadio; + private System.Windows.Forms.RadioButton colorDefaultRadio; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/EditNote.cs b/SourceGen/AppForms/EditNote.cs new file mode 100644 index 0000000..ca411ac --- /dev/null +++ b/SourceGen/AppForms/EditNote.cs @@ -0,0 +1,104 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Drawing; +using System.Windows.Forms; + +namespace SourceGen.AppForms { + public partial class EditNote : Form { + /// + /// Get or set the note object. On exit, will be set to null if the user wants + /// to delete the note. + /// + public MultiLineComment Note { get; set; } + + // Highlight color palette. Unless the user has funky theme, the color will be + // replacing a white background, and will be overlaid with black text, so should + // be on the lighter end of the spectrum. + private enum ColorList { + None = 0, Green, Blue, Yellow, Pink, Orange + } + private static Color[] sColors = new Color[] { + Color.FromArgb(0), // None + Color.LightGreen, + Color.LightBlue, + Color.Yellow, //LightGoldenrodYellow, + Color.LightPink, + Color.Orange + }; + private RadioButton[] mColorButtons; + + + public EditNote() { + InitializeComponent(); + Note = new MultiLineComment(string.Empty); + } + + private void EditNote_Load(object sender, EventArgs e) { + noteTextBox.Text = Note.Text; + + mColorButtons = new RadioButton[] { + colorDefaultRadio, + colorGreenRadio, + colorBlueRadio, + colorYellowRadio, + colorPinkRadio, + colorOrangeRadio + }; + Debug.Assert(mColorButtons.Length == sColors.Length); + + // Configure radio buttons. + colorDefaultRadio.Checked = true; + if (Note != null) { + Color curColor = Note.BackgroundColor; + for (int i = 0; i < sColors.Length; i++) { + // Can't just compare colors, because the sColors entries are "known" and + // have some additional properties set. Comparing the RGB values works. + if (sColors[i].ToArgb() == curColor.ToArgb()) { + mColorButtons[i].Checked = true; + break; + } + } + } + } + + // Handle Ctrl+Enter. + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { + if (keyData == (Keys.Control | Keys.Enter)) { + DialogResult = DialogResult.OK; + Close(); + return true; + } + return base.ProcessCmdKey(ref msg, keyData); + } + + private void EditNote_FormClosing(object sender, FormClosingEventArgs e) { + if (string.IsNullOrEmpty(noteTextBox.Text)) { + Note = null; + } else { + Color bkgndColor = Color.Fuchsia; + for (int i = 0; i < mColorButtons.Length; i++) { + if (mColorButtons[i].Checked) { + bkgndColor = sColors[i]; + break; + } + } + Note = new MultiLineComment(noteTextBox.Text, bkgndColor); + } + } + } +} diff --git a/SourceGen/AppForms/EditNote.resx b/SourceGen/AppForms/EditNote.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/EditNote.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/EditOperand.Designer.cs b/SourceGen/AppForms/EditOperand.Designer.cs new file mode 100644 index 0000000..2d29147 --- /dev/null +++ b/SourceGen/AppForms/EditOperand.Designer.cs @@ -0,0 +1,382 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class EditOperand { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.cancelButton = new System.Windows.Forms.Button(); + this.okButton = new System.Windows.Forms.Button(); + this.mainRadioPanel = new System.Windows.Forms.Panel(); + this.symbolPartPanel = new System.Windows.Forms.Panel(); + this.radioButtonBank = new System.Windows.Forms.RadioButton(); + this.radioButtonHigh = new System.Windows.Forms.RadioButton(); + this.radioButtonLow = new System.Windows.Forms.RadioButton(); + this.symbolTextBox = new System.Windows.Forms.TextBox(); + this.radioButtonSymbol = new System.Windows.Forms.RadioButton(); + this.radioButtonAscii = new System.Windows.Forms.RadioButton(); + this.radioButtonBinary = new System.Windows.Forms.RadioButton(); + this.radioButtonDecimal = new System.Windows.Forms.RadioButton(); + this.radioButtonHex = new System.Windows.Forms.RadioButton(); + this.radioButtonDefault = new System.Windows.Forms.RadioButton(); + this.selectFormatLabel = new System.Windows.Forms.Label(); + this.previewLabel = new System.Windows.Forms.Label(); + this.previewTextBox = new System.Windows.Forms.TextBox(); + this.symbolShortcutsGroupBox = new System.Windows.Forms.GroupBox(); + this.operandAndProjRadioButton = new System.Windows.Forms.RadioButton(); + this.operandAndLabelRadioButton = new System.Windows.Forms.RadioButton(); + this.labelInsteadRadioButton = new System.Windows.Forms.RadioButton(); + this.operandOnlyRadioButton = new System.Windows.Forms.RadioButton(); + this.mainRadioPanel.SuspendLayout(); + this.symbolPartPanel.SuspendLayout(); + this.symbolShortcutsGroupBox.SuspendLayout(); + this.SuspendLayout(); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(199, 377); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 4; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(118, 377); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 3; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // mainRadioPanel + // + this.mainRadioPanel.Controls.Add(this.symbolPartPanel); + this.mainRadioPanel.Controls.Add(this.symbolTextBox); + this.mainRadioPanel.Controls.Add(this.radioButtonSymbol); + this.mainRadioPanel.Controls.Add(this.radioButtonAscii); + this.mainRadioPanel.Controls.Add(this.radioButtonBinary); + this.mainRadioPanel.Controls.Add(this.radioButtonDecimal); + this.mainRadioPanel.Controls.Add(this.radioButtonHex); + this.mainRadioPanel.Controls.Add(this.radioButtonDefault); + this.mainRadioPanel.Location = new System.Drawing.Point(12, 29); + this.mainRadioPanel.Name = "mainRadioPanel"; + this.mainRadioPanel.Size = new System.Drawing.Size(261, 173); + this.mainRadioPanel.TabIndex = 0; + // + // symbolPartPanel + // + this.symbolPartPanel.Controls.Add(this.radioButtonBank); + this.symbolPartPanel.Controls.Add(this.radioButtonHigh); + this.symbolPartPanel.Controls.Add(this.radioButtonLow); + this.symbolPartPanel.Location = new System.Drawing.Point(69, 150); + this.symbolPartPanel.Name = "symbolPartPanel"; + this.symbolPartPanel.Size = new System.Drawing.Size(154, 18); + this.symbolPartPanel.TabIndex = 7; + // + // radioButtonBank + // + this.radioButtonBank.AutoSize = true; + this.radioButtonBank.Location = new System.Drawing.Point(100, 0); + this.radioButtonBank.Name = "radioButtonBank"; + this.radioButtonBank.Size = new System.Drawing.Size(50, 17); + this.radioButtonBank.TabIndex = 2; + this.radioButtonBank.TabStop = true; + this.radioButtonBank.Text = "&Bank"; + this.radioButtonBank.UseVisualStyleBackColor = true; + this.radioButtonBank.CheckedChanged += new System.EventHandler(this.PartGroup_CheckedChanged); + // + // radioButtonHigh + // + this.radioButtonHigh.AutoSize = true; + this.radioButtonHigh.Location = new System.Drawing.Point(50, 0); + this.radioButtonHigh.Name = "radioButtonHigh"; + this.radioButtonHigh.Size = new System.Drawing.Size(47, 17); + this.radioButtonHigh.TabIndex = 1; + this.radioButtonHigh.TabStop = true; + this.radioButtonHigh.Text = "&High"; + this.radioButtonHigh.UseVisualStyleBackColor = true; + this.radioButtonHigh.CheckedChanged += new System.EventHandler(this.PartGroup_CheckedChanged); + // + // radioButtonLow + // + this.radioButtonLow.AutoSize = true; + this.radioButtonLow.Location = new System.Drawing.Point(0, 0); + this.radioButtonLow.Name = "radioButtonLow"; + this.radioButtonLow.Size = new System.Drawing.Size(45, 17); + this.radioButtonLow.TabIndex = 0; + this.radioButtonLow.TabStop = true; + this.radioButtonLow.Text = "&Low"; + this.radioButtonLow.UseVisualStyleBackColor = true; + this.radioButtonLow.CheckedChanged += new System.EventHandler(this.PartGroup_CheckedChanged); + // + // symbolTextBox + // + this.symbolTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.symbolTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.symbolTextBox.Location = new System.Drawing.Point(69, 124); + this.symbolTextBox.MaxLength = 128; + this.symbolTextBox.Name = "symbolTextBox"; + this.symbolTextBox.Size = new System.Drawing.Size(179, 20); + this.symbolTextBox.TabIndex = 6; + this.symbolTextBox.TextChanged += new System.EventHandler(this.symbolTextBox_TextChanged); + // + // radioButtonSymbol + // + this.radioButtonSymbol.AutoSize = true; + this.radioButtonSymbol.Location = new System.Drawing.Point(4, 124); + this.radioButtonSymbol.Name = "radioButtonSymbol"; + this.radioButtonSymbol.Size = new System.Drawing.Size(59, 17); + this.radioButtonSymbol.TabIndex = 5; + this.radioButtonSymbol.TabStop = true; + this.radioButtonSymbol.Text = "&Symbol"; + this.radioButtonSymbol.UseVisualStyleBackColor = true; + this.radioButtonSymbol.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radioButtonAscii + // + this.radioButtonAscii.AutoSize = true; + this.radioButtonAscii.Location = new System.Drawing.Point(4, 100); + this.radioButtonAscii.Name = "radioButtonAscii"; + this.radioButtonAscii.Size = new System.Drawing.Size(100, 17); + this.radioButtonAscii.TabIndex = 4; + this.radioButtonAscii.TabStop = true; + this.radioButtonAscii.Text = "&ASCII character"; + this.radioButtonAscii.UseVisualStyleBackColor = true; + this.radioButtonAscii.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radioButtonBinary + // + this.radioButtonBinary.AutoSize = true; + this.radioButtonBinary.Location = new System.Drawing.Point(4, 76); + this.radioButtonBinary.Name = "radioButtonBinary"; + this.radioButtonBinary.Size = new System.Drawing.Size(54, 17); + this.radioButtonBinary.TabIndex = 3; + this.radioButtonBinary.TabStop = true; + this.radioButtonBinary.Text = "&Binary"; + this.radioButtonBinary.UseVisualStyleBackColor = true; + this.radioButtonBinary.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radioButtonDecimal + // + this.radioButtonDecimal.AutoSize = true; + this.radioButtonDecimal.Location = new System.Drawing.Point(4, 52); + this.radioButtonDecimal.Name = "radioButtonDecimal"; + this.radioButtonDecimal.Size = new System.Drawing.Size(63, 17); + this.radioButtonDecimal.TabIndex = 2; + this.radioButtonDecimal.TabStop = true; + this.radioButtonDecimal.Text = "&Decimal"; + this.radioButtonDecimal.UseVisualStyleBackColor = true; + this.radioButtonDecimal.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radioButtonHex + // + this.radioButtonHex.AutoSize = true; + this.radioButtonHex.Location = new System.Drawing.Point(4, 28); + this.radioButtonHex.Name = "radioButtonHex"; + this.radioButtonHex.Size = new System.Drawing.Size(86, 17); + this.radioButtonHex.TabIndex = 1; + this.radioButtonHex.TabStop = true; + this.radioButtonHex.Text = "&Hexadecimal"; + this.radioButtonHex.UseVisualStyleBackColor = true; + this.radioButtonHex.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // radioButtonDefault + // + this.radioButtonDefault.AutoSize = true; + this.radioButtonDefault.Location = new System.Drawing.Point(4, 4); + this.radioButtonDefault.Name = "radioButtonDefault"; + this.radioButtonDefault.Size = new System.Drawing.Size(59, 17); + this.radioButtonDefault.TabIndex = 0; + this.radioButtonDefault.TabStop = true; + this.radioButtonDefault.Text = "&Default"; + this.radioButtonDefault.UseVisualStyleBackColor = true; + this.radioButtonDefault.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged); + // + // selectFormatLabel + // + this.selectFormatLabel.AutoSize = true; + this.selectFormatLabel.Location = new System.Drawing.Point(13, 13); + this.selectFormatLabel.Name = "selectFormatLabel"; + this.selectFormatLabel.Size = new System.Drawing.Size(114, 13); + this.selectFormatLabel.TabIndex = 0; + this.selectFormatLabel.Text = "Select operand format:"; + // + // previewLabel + // + this.previewLabel.AutoSize = true; + this.previewLabel.Location = new System.Drawing.Point(11, 210); + this.previewLabel.Name = "previewLabel"; + this.previewLabel.Size = new System.Drawing.Size(48, 13); + this.previewLabel.TabIndex = 1; + this.previewLabel.Text = "Preview:"; + // + // previewTextBox + // + this.previewTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.previewTextBox.Enabled = false; + this.previewTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.previewTextBox.Location = new System.Drawing.Point(66, 208); + this.previewTextBox.Name = "previewTextBox"; + this.previewTextBox.Size = new System.Drawing.Size(207, 20); + this.previewTextBox.TabIndex = 2; + // + // symbolShortcutsGroupBox + // + this.symbolShortcutsGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.symbolShortcutsGroupBox.Controls.Add(this.operandAndProjRadioButton); + this.symbolShortcutsGroupBox.Controls.Add(this.operandAndLabelRadioButton); + this.symbolShortcutsGroupBox.Controls.Add(this.labelInsteadRadioButton); + this.symbolShortcutsGroupBox.Controls.Add(this.operandOnlyRadioButton); + this.symbolShortcutsGroupBox.Location = new System.Drawing.Point(13, 248); + this.symbolShortcutsGroupBox.Name = "symbolShortcutsGroupBox"; + this.symbolShortcutsGroupBox.Size = new System.Drawing.Size(261, 118); + this.symbolShortcutsGroupBox.TabIndex = 5; + this.symbolShortcutsGroupBox.TabStop = false; + this.symbolShortcutsGroupBox.Text = "Symbol Shortcuts"; + // + // operandAndProjRadioButton + // + this.operandAndProjRadioButton.AutoSize = true; + this.operandAndProjRadioButton.Location = new System.Drawing.Point(7, 92); + this.operandAndProjRadioButton.Name = "operandAndProjRadioButton"; + this.operandAndProjRadioButton.Size = new System.Drawing.Size(212, 17); + this.operandAndProjRadioButton.TabIndex = 3; + this.operandAndProjRadioButton.TabStop = true; + this.operandAndProjRadioButton.Text = "Set operand AND create &project symbol"; + this.operandAndProjRadioButton.UseVisualStyleBackColor = true; + // + // operandAndLabelRadioButton + // + this.operandAndLabelRadioButton.AutoSize = true; + this.operandAndLabelRadioButton.Location = new System.Drawing.Point(7, 68); + this.operandAndLabelRadioButton.Name = "operandAndLabelRadioButton"; + this.operandAndLabelRadioButton.Size = new System.Drawing.Size(249, 17); + this.operandAndLabelRadioButton.TabIndex = 2; + this.operandAndLabelRadioButton.TabStop = true; + this.operandAndLabelRadioButton.Text = "Set &operand AND create label at target address"; + this.operandAndLabelRadioButton.UseVisualStyleBackColor = true; + // + // labelInsteadRadioButton + // + this.labelInsteadRadioButton.AutoSize = true; + this.labelInsteadRadioButton.Location = new System.Drawing.Point(7, 44); + this.labelInsteadRadioButton.Name = "labelInsteadRadioButton"; + this.labelInsteadRadioButton.Size = new System.Drawing.Size(200, 17); + this.labelInsteadRadioButton.TabIndex = 1; + this.labelInsteadRadioButton.TabStop = true; + this.labelInsteadRadioButton.Text = "&Create label at target address instead"; + this.labelInsteadRadioButton.UseVisualStyleBackColor = true; + // + // operandOnlyRadioButton + // + this.operandOnlyRadioButton.AutoSize = true; + this.operandOnlyRadioButton.Location = new System.Drawing.Point(7, 20); + this.operandOnlyRadioButton.Name = "operandOnlyRadioButton"; + this.operandOnlyRadioButton.Size = new System.Drawing.Size(162, 17); + this.operandOnlyRadioButton.TabIndex = 0; + this.operandOnlyRadioButton.TabStop = true; + this.operandOnlyRadioButton.Text = "&Just set the operand (default)"; + this.operandOnlyRadioButton.UseVisualStyleBackColor = true; + // + // EditOperand + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(286, 412); + this.Controls.Add(this.symbolShortcutsGroupBox); + this.Controls.Add(this.previewTextBox); + this.Controls.Add(this.previewLabel); + this.Controls.Add(this.selectFormatLabel); + this.Controls.Add(this.mainRadioPanel); + this.Controls.Add(this.okButton); + this.Controls.Add(this.cancelButton); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditOperand"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Operand"; + this.Load += new System.EventHandler(this.EditOperand_Load); + this.Shown += new System.EventHandler(this.EditOperand_Shown); + this.mainRadioPanel.ResumeLayout(false); + this.mainRadioPanel.PerformLayout(); + this.symbolPartPanel.ResumeLayout(false); + this.symbolPartPanel.PerformLayout(); + this.symbolShortcutsGroupBox.ResumeLayout(false); + this.symbolShortcutsGroupBox.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Panel mainRadioPanel; + private System.Windows.Forms.TextBox symbolTextBox; + private System.Windows.Forms.RadioButton radioButtonSymbol; + private System.Windows.Forms.RadioButton radioButtonAscii; + private System.Windows.Forms.RadioButton radioButtonBinary; + private System.Windows.Forms.RadioButton radioButtonDecimal; + private System.Windows.Forms.RadioButton radioButtonHex; + private System.Windows.Forms.RadioButton radioButtonDefault; + private System.Windows.Forms.Label selectFormatLabel; + private System.Windows.Forms.Label previewLabel; + private System.Windows.Forms.TextBox previewTextBox; + private System.Windows.Forms.Panel symbolPartPanel; + private System.Windows.Forms.RadioButton radioButtonBank; + private System.Windows.Forms.RadioButton radioButtonHigh; + private System.Windows.Forms.RadioButton radioButtonLow; + private System.Windows.Forms.GroupBox symbolShortcutsGroupBox; + private System.Windows.Forms.RadioButton operandAndProjRadioButton; + private System.Windows.Forms.RadioButton operandAndLabelRadioButton; + private System.Windows.Forms.RadioButton labelInsteadRadioButton; + private System.Windows.Forms.RadioButton operandOnlyRadioButton; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/EditOperand.cs b/SourceGen/AppForms/EditOperand.cs new file mode 100644 index 0000000..4f2a4de --- /dev/null +++ b/SourceGen/AppForms/EditOperand.cs @@ -0,0 +1,531 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Text; +using System.Windows.Forms; + +using Asm65; + +namespace SourceGen.AppForms { + public partial class EditOperand : Form { + /// + /// In/out. May be null on entry if the offset doesn't have a format descriptor + /// specified. Will be null on exit if "default" is selected. + /// + public FormatDescriptor FormatDescriptor { get; set; } + + public enum SymbolShortcutAction { + None = 0, CreateLabelInstead, CreateLabelAlso, CreateProjectSymbolAlso + } + + /// + /// Remember the last option we used. + /// + private static SymbolShortcutAction sLastAction = SymbolShortcutAction.None; + + /// + /// On OK dialog exit, specifies that an additional action should be taken. + /// + public SymbolShortcutAction ShortcutAction { get; private set; } + + /// + /// Additional argument, meaning dependent on ShortcutAction. This will either be + /// the target label offset or the project symbol value. + /// + public int ShortcutArg { get; private set; } + + /// + /// Width of full instruction, including opcode. + /// + private int mInstructionLength; + + /// + /// Number of hexadecimal digits to show in the preview. Sometimes you want + /// to force this to be longer or shorter than InstructionLength would indicate, + /// e.g. "BRA $1000" has a 1-byte operand. + /// + private int mPreviewHexDigits; + + /// + /// Operand value, extracted from file data. For a relative branch, this will be + /// an address instead. Only used for preview window. + /// + private int mOperandValue; + + /// + /// Is the operand an immediate value? If so, we enable the symbol part selection. + /// + private bool mIsExtendedImmediate; + + /// + /// Is the operand a PC relative offset? + /// + private bool mIsPcRelative; + + /// + /// Special handling for block move instructions (MVN/MVP). + /// + private bool mIsBlockMove; + + /// + /// If set, show a '#' in the preview indow. + /// + private bool mShowHashPrefix; + + ///// + ///// Symbol table to use when resolving symbolic values. + ///// + //private SymbolTable SymbolTable { get; set; } + + /// + /// Project reference. + /// + private DisasmProject mProject; + + /// + /// Formatter to use when displaying addresses and hex values. + /// + private Formatter mFormatter; + + /// + /// Copy of operand Anattribs. + /// + private Anattrib mAttr; + + /// + /// Set this during initial control configuration, so we know to ignore the CheckedChanged + /// events. + /// + private bool mIsInitialSetup; + + /// + /// Set to true if the user has entered a symbol that matches an auto-generated symbol. + /// + private bool mIsSymbolAuto; + + + public EditOperand(int offset, DisasmProject project, Asm65.Formatter formatter) { + InitializeComponent(); + + mProject = project; + mFormatter = formatter; + + // Configure the appearance. + mAttr = mProject.GetAnattrib(offset); + OpDef op = mProject.CpuDef.GetOpDef(mProject.FileData[offset]); + mInstructionLength = mAttr.Length; + mPreviewHexDigits = (mAttr.Length - 1) * 2; + if (mAttr.OperandAddress >= 0) { + // Use this as the operand value when available. This lets us present + // relative branch instructions in the expected form. + mOperandValue = mAttr.OperandAddress; + + if (op.AddrMode == OpDef.AddressMode.PCRel) { + mPreviewHexDigits = 4; + mIsPcRelative = true; + } else if (op.AddrMode == OpDef.AddressMode.PCRelLong || + op.AddrMode == OpDef.AddressMode.StackPCRelLong) { + mIsPcRelative = true; + } + } else { + int opVal = op.GetOperand(mProject.FileData, offset, mAttr.StatusFlags); + mOperandValue = opVal; + if (op.AddrMode == OpDef.AddressMode.BlockMove) { + // MVN and MVP screw things up by having two operands in one instruction. + // We deal with this by passing in the value from the second byte + // (source bank) as the value, and applying the chosen format to both bytes. + mIsBlockMove = true; + mOperandValue = opVal >> 8; + mPreviewHexDigits = 2; + } + } + mIsExtendedImmediate = op.IsExtendedImmediate; // Imm, PEA, MVN/MVP + mShowHashPrefix = op.IsImmediate; // just Imm + } + + private void EditOperand_Load(object sender, EventArgs e) { + mIsInitialSetup = true; + + // Can this be represented as high or low ASCII? + radioButtonAscii.Enabled = CommonUtil.TextUtil.IsHiLoAscii(mOperandValue); + + // Configure the dialog from the FormatDescriptor, if one is available. + SetControlsFromDescriptor(FormatDescriptor); + + // Do this whether or not symbol is checked -- want to have this set when the + // dialog is initially in default format. + switch (sLastAction) { + case SymbolShortcutAction.CreateLabelInstead: + labelInsteadRadioButton.Checked = true; + break; + case SymbolShortcutAction.CreateLabelAlso: + operandAndLabelRadioButton.Checked = true; + break; + case SymbolShortcutAction.CreateProjectSymbolAlso: + operandAndProjRadioButton.Checked = true; + break; + default: + operandOnlyRadioButton.Checked = true; + break; + } + + mIsInitialSetup = false; + UpdateControls(); + } + + private void EditOperand_Shown(object sender, EventArgs e) { + // Start with the focus in the text box. This way they can start typing + // immediately. + symbolTextBox.Focus(); + } + + private void symbolTextBox_TextChanged(object sender, EventArgs e) { + // Make sure Symbol is checked if they're typing text in. + radioButtonSymbol.Checked = true; + UpdateControls(); + } + + /// + /// Handles CheckedChanged event for all radio buttons in main group. + /// + private void MainGroup_CheckedChanged(object sender, EventArgs e) { + // Enable/disable the low/high/bank radio group. + // Update preview window. + UpdateControls(); + } + + /// + /// Handles CheckedChanged event for all radio buttons in symbol-part group. + /// + private void PartGroup_CheckedChanged(object sender, EventArgs e) { + // Update preview window. + UpdateControls(); + } + + private void okButton_Click(object sender, EventArgs e) { + FormatDescriptor = CreateDescriptorFromControls(); + + // + // Extract the current shortcut action. For dialog configuration purposes we + // want to capture the current state. For the caller, we force it to "none" + // if we're not using a symbol format. + // + SymbolShortcutAction action = SymbolShortcutAction.None; + if (labelInsteadRadioButton.Checked) { + action = SymbolShortcutAction.CreateLabelInstead; + } else if (operandAndLabelRadioButton.Checked) { + action = SymbolShortcutAction.CreateLabelAlso; + } else if (operandAndProjRadioButton.Checked) { + action = SymbolShortcutAction.CreateProjectSymbolAlso; + } else if (operandOnlyRadioButton.Checked) { + action = SymbolShortcutAction.None; + } else { + Debug.Assert(false); + action = SymbolShortcutAction.None; + } + sLastAction = action; + + if (radioButtonSymbol.Checked && FormatDescriptor != null) { + // Only report a shortcut action if they've entered a symbol. If they + // checked symbol but left the field blank, they're just trying to delete + // the format. + ShortcutAction = action; + } else { + ShortcutAction = SymbolShortcutAction.None; + } + } + + /// + /// Updates all of the controls to reflect the current internal state. + /// + private void UpdateControls() { + if (mIsInitialSetup) { + return; + } + symbolPartPanel.Enabled = radioButtonSymbol.Checked && mIsExtendedImmediate; + symbolShortcutsGroupBox.Enabled = radioButtonSymbol.Checked; + + SetPreviewText(); + + bool isOk = true; + if (radioButtonSymbol.Checked) { + // Just check for correct format. References to non-existent labels are allowed. + // + // We try to block references to auto labels, but it's possible to get around it + // (replace auto label with user label, reference non-existent auto label, + // remove user label). We could try harder, but currently not necessary. + isOk = !mIsSymbolAuto && Asm65.Label.ValidateLabel(symbolTextBox.Text); + + // Allow empty strings as a way to delete the label and return to "default". + if (string.IsNullOrEmpty(symbolTextBox.Text)) { + isOk = true; + } + + ConfigureSymbolShortcuts(); + } + okButton.Enabled = isOk; + } + + /// + /// Sets the text displayed in the "preview" text box. + /// + private void SetPreviewText() { + //symbolValueLabel.Text = string.Empty; + mIsSymbolAuto = false; + + FormatDescriptor dfd = CreateDescriptorFromControls(); + if (dfd == null) { + // Default format. We can't actually know what this look like, so just + // clear the box. + previewTextBox.Text = string.Empty; + return; + } + + if (dfd.FormatSubType == FormatDescriptor.SubType.Symbol && + string.IsNullOrEmpty(dfd.SymbolRef.Label)) { + // no label yet, nothing to show + previewTextBox.Text = string.Empty; + return; + } + + StringBuilder preview = new StringBuilder(); + if (mShowHashPrefix) { + preview.Append('#'); + } + + switch (dfd.FormatSubType) { + case FormatDescriptor.SubType.Hex: + preview.Append(mFormatter.FormatHexValue(mOperandValue, mPreviewHexDigits)); + break; + case FormatDescriptor.SubType.Decimal: + preview.Append(mFormatter.FormatDecimalValue(mOperandValue)); + break; + case FormatDescriptor.SubType.Binary: + preview.Append(mFormatter.FormatBinaryValue(mOperandValue, 8)); + break; + case FormatDescriptor.SubType.Ascii: + preview.Append(mFormatter.FormatAsciiOrHex(mOperandValue)); + break; + case FormatDescriptor.SubType.Symbol: + if (mProject.SymbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym)) { + if (mIsBlockMove) { + // For a 24-bit symbol, we grab the high byte. This is the + // expected behavior, according to Eyes & Lichty; see the + // explanation of the MVP instruction. For an 8-bit symbol + // the assembler just takes the value. + // TODO(someday): allow a different symbol for each part of the + // operand. + if (sym.Value > 0xff) { + radioButtonBank.Checked = true; + } else { + radioButtonLow.Checked = true; + } + dfd = CreateDescriptorFromControls(); + } + + // Hack to make relative branches look right in the preview window. + // Otherwise they show up like " + /// Configures the buttons in the "symbol shortcuts" group box. The entire box is + /// disabled unless "symbol" is selected. Other options are selectively enabled or + /// disabled as appropriate for the current input. If we disable the selection option, + /// the selection will be reset to default. + /// + private void ConfigureSymbolShortcuts() { + // operandOnlyRadioButton: always enabled + // labelInsteadRadioButton: symbol is unknown and operand address has no label + // operandAndLabelRadioButton: same as labelInstead + // operandAndProjRadioButton: symbol is unknown and operand address is outside project + + string labelStr = symbolTextBox.Text; + ShortcutArg = -1; + + // Is this a known symbol? If so, disable most options and bail. + if (mProject.SymbolTable.TryGetValue(labelStr, out Symbol sym)) { + labelInsteadRadioButton.Enabled = operandAndLabelRadioButton.Enabled = + operandAndProjRadioButton.Enabled = false; + operandOnlyRadioButton.Checked = true; + return; + } + + if (mAttr.OperandOffset >= 0) { + // Operand target is inside the file. Does the target offset already have a label? + bool hasLabel = mProject.UserLabels.ContainsKey(mAttr.OperandOffset); + labelInsteadRadioButton.Enabled = operandAndLabelRadioButton.Enabled = + !hasLabel; + operandAndProjRadioButton.Enabled = false; + ShortcutArg = mAttr.OperandOffset; + } else if (mAttr.OperandAddress >= 0) { + // Operand target is outside the file. + labelInsteadRadioButton.Enabled = operandAndLabelRadioButton.Enabled = false; + operandAndProjRadioButton.Enabled = true; + ShortcutArg = mAttr.OperandAddress; + } else { + // Probably an immediate operand. + labelInsteadRadioButton.Enabled = operandAndLabelRadioButton.Enabled = + operandAndProjRadioButton.Enabled = false; + } + + // Select the default option if the currently-selected option is no longer available. + if ((labelInsteadRadioButton.Checked && !labelInsteadRadioButton.Enabled) || + (operandAndLabelRadioButton.Checked && !operandAndLabelRadioButton.Enabled) || + (operandAndProjRadioButton.Checked && !operandAndProjRadioButton.Enabled)) { + operandOnlyRadioButton.Checked = true; + } + } + + /// + /// Configures the dialog controls based on the provided format descriptor. + /// + /// FormatDescriptor to use. + private void SetControlsFromDescriptor(FormatDescriptor dfd) { + Debug.Assert(mIsInitialSetup); + radioButtonLow.Checked = true; + + if (dfd == null) { + radioButtonDefault.Checked = true; + return; + } + + // NOTE: it's entirely possible to have a weird format (e.g. string) if the + // instruction used to be hinted as data. Handle it gracefully. + switch (dfd.FormatType) { + case FormatDescriptor.Type.NumericLE: + switch (dfd.FormatSubType) { + case FormatDescriptor.SubType.Hex: + radioButtonHex.Checked = true; + break; + case FormatDescriptor.SubType.Decimal: + radioButtonDecimal.Checked = true; + break; + case FormatDescriptor.SubType.Binary: + radioButtonBinary.Checked = true; + break; + case FormatDescriptor.SubType.Ascii: + radioButtonAscii.Checked = true; + break; + case FormatDescriptor.SubType.Symbol: + Debug.Assert(dfd.HasSymbol); + radioButtonSymbol.Checked = true; + switch (dfd.SymbolRef.ValuePart) { + case WeakSymbolRef.Part.Low: + radioButtonLow.Checked = true; + break; + case WeakSymbolRef.Part.High: + radioButtonHigh.Checked = true; + break; + case WeakSymbolRef.Part.Bank: + radioButtonBank.Checked = true; + break; + default: + Debug.Assert(false); + break; + } + symbolTextBox.Text = dfd.SymbolRef.Label; + break; + case FormatDescriptor.SubType.None: + default: + // Unexpected; call it hex. + radioButtonHex.Checked = true; + break; + } + break; + case FormatDescriptor.Type.NumericBE: + case FormatDescriptor.Type.String: + case FormatDescriptor.Type.Fill: + default: + // Unexpected; used to be data? + radioButtonDefault.Checked = true; + break; + } + } + + /// + /// Creates a FormatDescriptor from the current state of the dialog controls. + /// + /// New FormatDescriptor. + private FormatDescriptor CreateDescriptorFromControls() { + if (radioButtonSymbol.Checked) { + if (string.IsNullOrEmpty(symbolTextBox.Text)) { + // empty symbol --> default format (intuitive way to delete label reference) + return null; + } + WeakSymbolRef.Part part; + if (radioButtonLow.Checked) { + part = WeakSymbolRef.Part.Low; + } else if (radioButtonHigh.Checked) { + part = WeakSymbolRef.Part.High; + } else if (radioButtonBank.Checked) { + part = WeakSymbolRef.Part.Bank; + } else { + Debug.Assert(false); + part = WeakSymbolRef.Part.Low; + } + return FormatDescriptor.Create(mInstructionLength, + new WeakSymbolRef(symbolTextBox.Text, part), false); + } + + FormatDescriptor.SubType subType; + if (radioButtonDefault.Checked) { + return null; + } else if (radioButtonHex.Checked) { + subType = FormatDescriptor.SubType.Hex; + } else if (radioButtonDecimal.Checked) { + subType = FormatDescriptor.SubType.Decimal; + } else if (radioButtonBinary.Checked) { + subType = FormatDescriptor.SubType.Binary; + } else if (radioButtonAscii.Checked) { + subType = FormatDescriptor.SubType.Ascii; + } else if (radioButtonSymbol.Checked) { + subType = FormatDescriptor.SubType.Symbol; + } else { + Debug.Assert(false); + subType = FormatDescriptor.SubType.None; + } + + return FormatDescriptor.Create(mInstructionLength, + FormatDescriptor.Type.NumericLE, subType); + } + } +} diff --git a/SourceGen/AppForms/EditOperand.resx b/SourceGen/AppForms/EditOperand.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/EditOperand.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/EditProjectProperties.Designer.cs b/SourceGen/AppForms/EditProjectProperties.Designer.cs new file mode 100644 index 0000000..cc371d4 --- /dev/null +++ b/SourceGen/AppForms/EditProjectProperties.Designer.cs @@ -0,0 +1,632 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class EditProjectProperties { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + System.Windows.Forms.ListViewItem listViewItem1 = new System.Windows.Forms.ListViewItem(new string[] { + "0123456789AB", + "%00000000", + "Const", + "This is a test to gauge column widths"}, -1); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.generalTab = new System.Windows.Forms.TabPage(); + this.analysisGroupBox = new System.Windows.Forms.GroupBox(); + this.seekAltTargetCheckBox = new System.Windows.Forms.CheckBox(); + this.minStringCharsComboBox = new System.Windows.Forms.ComboBox(); + this.minCharsForStringLabel = new System.Windows.Forms.Label(); + this.analyzeUncategorizedCheckBox = new System.Windows.Forms.CheckBox(); + this.entryFlagsGroupBox = new System.Windows.Forms.GroupBox(); + this.flagsLabel = new System.Windows.Forms.Label(); + this.currentFlagsLabel = new System.Windows.Forms.Label(); + this.changeFlagButton = new System.Windows.Forms.Button(); + this.cpuGroupBox = new System.Windows.Forms.GroupBox(); + this.undocInstrCheckBox = new System.Windows.Forms.CheckBox(); + this.cpuComboBox = new System.Windows.Forms.ComboBox(); + this.symbolsTab = new System.Windows.Forms.TabPage(); + this.importSymbolsButton = new System.Windows.Forms.Button(); + this.editSymbolButton = new System.Windows.Forms.Button(); + this.removeSymbolButton = new System.Windows.Forms.Button(); + this.newSymbolButton = new System.Windows.Forms.Button(); + this.symbolsDefinedLabel = new System.Windows.Forms.Label(); + this.projectSymbolsListView = new System.Windows.Forms.ListView(); + this.nameColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.valueColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.typeColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.commentColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.symbolFilesTab = new System.Windows.Forms.TabPage(); + this.symbolFileDownButton = new System.Windows.Forms.Button(); + this.symbolFileUpButton = new System.Windows.Forms.Button(); + this.addSymbolFilesButton = new System.Windows.Forms.Button(); + this.symbolFileRemoveButton = new System.Windows.Forms.Button(); + this.symbolFilesListBox = new System.Windows.Forms.ListBox(); + this.configuredFilesLabel = new System.Windows.Forms.Label(); + this.extensionScriptsTab = new System.Windows.Forms.TabPage(); + this.extensionScriptRemoveButton = new System.Windows.Forms.Button(); + this.addExtensionScriptsButton = new System.Windows.Forms.Button(); + this.extensionScriptsListBox = new System.Windows.Forms.ListBox(); + this.configuredScriptsLabel = new System.Windows.Forms.Label(); + this.labelUndoRedoNote = new System.Windows.Forms.Label(); + this.cancelButton = new System.Windows.Forms.Button(); + this.okButton = new System.Windows.Forms.Button(); + this.applyButton = new System.Windows.Forms.Button(); + this.tabControl1.SuspendLayout(); + this.generalTab.SuspendLayout(); + this.analysisGroupBox.SuspendLayout(); + this.entryFlagsGroupBox.SuspendLayout(); + this.cpuGroupBox.SuspendLayout(); + this.symbolsTab.SuspendLayout(); + this.symbolFilesTab.SuspendLayout(); + this.extensionScriptsTab.SuspendLayout(); + this.SuspendLayout(); + // + // tabControl1 + // + this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabControl1.Controls.Add(this.generalTab); + this.tabControl1.Controls.Add(this.symbolsTab); + this.tabControl1.Controls.Add(this.symbolFilesTab); + this.tabControl1.Controls.Add(this.extensionScriptsTab); + this.tabControl1.Location = new System.Drawing.Point(2, 2); + this.tabControl1.Multiline = true; + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(622, 318); + this.tabControl1.TabIndex = 0; + // + // generalTab + // + this.generalTab.Controls.Add(this.analysisGroupBox); + this.generalTab.Controls.Add(this.entryFlagsGroupBox); + this.generalTab.Controls.Add(this.cpuGroupBox); + this.generalTab.Location = new System.Drawing.Point(4, 22); + this.generalTab.Name = "generalTab"; + this.generalTab.Padding = new System.Windows.Forms.Padding(3); + this.generalTab.Size = new System.Drawing.Size(614, 292); + this.generalTab.TabIndex = 0; + this.generalTab.Text = "General"; + this.generalTab.UseVisualStyleBackColor = true; + // + // analysisGroupBox + // + this.analysisGroupBox.Controls.Add(this.seekAltTargetCheckBox); + this.analysisGroupBox.Controls.Add(this.minStringCharsComboBox); + this.analysisGroupBox.Controls.Add(this.minCharsForStringLabel); + this.analysisGroupBox.Controls.Add(this.analyzeUncategorizedCheckBox); + this.analysisGroupBox.Location = new System.Drawing.Point(225, 7); + this.analysisGroupBox.Name = "analysisGroupBox"; + this.analysisGroupBox.Size = new System.Drawing.Size(204, 163); + this.analysisGroupBox.TabIndex = 2; + this.analysisGroupBox.TabStop = false; + this.analysisGroupBox.Text = "Analysis Parameters"; + // + // seekAltTargetCheckBox + // + this.seekAltTargetCheckBox.AutoSize = true; + this.seekAltTargetCheckBox.Location = new System.Drawing.Point(7, 45); + this.seekAltTargetCheckBox.Name = "seekAltTargetCheckBox"; + this.seekAltTargetCheckBox.Size = new System.Drawing.Size(130, 17); + this.seekAltTargetCheckBox.TabIndex = 3; + this.seekAltTargetCheckBox.Text = "Seek alternate targets"; + this.seekAltTargetCheckBox.UseVisualStyleBackColor = true; + this.seekAltTargetCheckBox.CheckedChanged += new System.EventHandler(this.seekAltTargetCheckBox_CheckedChanged); + // + // minStringCharsComboBox + // + this.minStringCharsComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.minStringCharsComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.minStringCharsComboBox.FormattingEnabled = true; + this.minStringCharsComboBox.Items.AddRange(new object[] { + "None (disabled)", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10"}); + this.minStringCharsComboBox.Location = new System.Drawing.Point(7, 88); + this.minStringCharsComboBox.Name = "minStringCharsComboBox"; + this.minStringCharsComboBox.Size = new System.Drawing.Size(191, 21); + this.minStringCharsComboBox.TabIndex = 2; + this.minStringCharsComboBox.SelectedIndexChanged += new System.EventHandler(this.minStringCharsComboBox_SelectedIndexChanged); + // + // minCharsForStringLabel + // + this.minCharsForStringLabel.AutoSize = true; + this.minCharsForStringLabel.Location = new System.Drawing.Point(7, 71); + this.minCharsForStringLabel.Name = "minCharsForStringLabel"; + this.minCharsForStringLabel.Size = new System.Drawing.Size(147, 13); + this.minCharsForStringLabel.TabIndex = 1; + this.minCharsForStringLabel.Text = "Minimum characters for string:"; + // + // analyzeUncategorizedCheckBox + // + this.analyzeUncategorizedCheckBox.AutoSize = true; + this.analyzeUncategorizedCheckBox.Location = new System.Drawing.Point(7, 21); + this.analyzeUncategorizedCheckBox.Name = "analyzeUncategorizedCheckBox"; + this.analyzeUncategorizedCheckBox.Size = new System.Drawing.Size(157, 17); + this.analyzeUncategorizedCheckBox.TabIndex = 0; + this.analyzeUncategorizedCheckBox.Text = "Analyze uncategorized data"; + this.analyzeUncategorizedCheckBox.UseVisualStyleBackColor = true; + this.analyzeUncategorizedCheckBox.CheckedChanged += new System.EventHandler(this.analyzeUncategorizedCheckBox_CheckedChanged); + // + // entryFlagsGroupBox + // + this.entryFlagsGroupBox.Controls.Add(this.flagsLabel); + this.entryFlagsGroupBox.Controls.Add(this.currentFlagsLabel); + this.entryFlagsGroupBox.Controls.Add(this.changeFlagButton); + this.entryFlagsGroupBox.Location = new System.Drawing.Point(7, 92); + this.entryFlagsGroupBox.Name = "entryFlagsGroupBox"; + this.entryFlagsGroupBox.Size = new System.Drawing.Size(204, 78); + this.entryFlagsGroupBox.TabIndex = 1; + this.entryFlagsGroupBox.TabStop = false; + this.entryFlagsGroupBox.Text = "Entry Flags"; + // + // flagsLabel + // + this.flagsLabel.AutoSize = true; + this.flagsLabel.Location = new System.Drawing.Point(7, 20); + this.flagsLabel.Name = "flagsLabel"; + this.flagsLabel.Size = new System.Drawing.Size(35, 13); + this.flagsLabel.TabIndex = 0; + this.flagsLabel.Text = "Flags:"; + // + // currentFlagsLabel + // + this.currentFlagsLabel.AutoSize = true; + this.currentFlagsLabel.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.currentFlagsLabel.Location = new System.Drawing.Point(38, 21); + this.currentFlagsLabel.Name = "currentFlagsLabel"; + this.currentFlagsLabel.Size = new System.Drawing.Size(163, 13); + this.currentFlagsLabel.TabIndex = 1; + this.currentFlagsLabel.Text = "N- V- M- X- D- I- Z- C- E-"; + // + // changeFlagButton + // + this.changeFlagButton.Location = new System.Drawing.Point(10, 42); + this.changeFlagButton.Name = "changeFlagButton"; + this.changeFlagButton.Size = new System.Drawing.Size(91, 23); + this.changeFlagButton.TabIndex = 2; + this.changeFlagButton.Text = "Change"; + this.changeFlagButton.UseVisualStyleBackColor = true; + this.changeFlagButton.Click += new System.EventHandler(this.changeFlagButton_Click); + // + // cpuGroupBox + // + this.cpuGroupBox.Controls.Add(this.undocInstrCheckBox); + this.cpuGroupBox.Controls.Add(this.cpuComboBox); + this.cpuGroupBox.Location = new System.Drawing.Point(7, 7); + this.cpuGroupBox.Name = "cpuGroupBox"; + this.cpuGroupBox.Size = new System.Drawing.Size(204, 78); + this.cpuGroupBox.TabIndex = 0; + this.cpuGroupBox.TabStop = false; + this.cpuGroupBox.Text = "CPU"; + // + // undocInstrCheckBox + // + this.undocInstrCheckBox.AutoSize = true; + this.undocInstrCheckBox.Location = new System.Drawing.Point(7, 47); + this.undocInstrCheckBox.Name = "undocInstrCheckBox"; + this.undocInstrCheckBox.Size = new System.Drawing.Size(189, 17); + this.undocInstrCheckBox.TabIndex = 1; + this.undocInstrCheckBox.Text = "Enable undocumented instructions"; + this.undocInstrCheckBox.UseVisualStyleBackColor = true; + this.undocInstrCheckBox.CheckedChanged += new System.EventHandler(this.undocInstrCheckBox_CheckedChanged); + // + // cpuComboBox + // + this.cpuComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cpuComboBox.FormattingEnabled = true; + this.cpuComboBox.Items.AddRange(new object[] { + "MOS 6502", + "WDC W65C02S", + "WDC W65C816S"}); + this.cpuComboBox.Location = new System.Drawing.Point(6, 19); + this.cpuComboBox.Name = "cpuComboBox"; + this.cpuComboBox.Size = new System.Drawing.Size(190, 21); + this.cpuComboBox.TabIndex = 0; + this.cpuComboBox.SelectedIndexChanged += new System.EventHandler(this.cpuComboBox_SelectedIndexChanged); + // + // symbolsTab + // + this.symbolsTab.Controls.Add(this.importSymbolsButton); + this.symbolsTab.Controls.Add(this.editSymbolButton); + this.symbolsTab.Controls.Add(this.removeSymbolButton); + this.symbolsTab.Controls.Add(this.newSymbolButton); + this.symbolsTab.Controls.Add(this.symbolsDefinedLabel); + this.symbolsTab.Controls.Add(this.projectSymbolsListView); + this.symbolsTab.Location = new System.Drawing.Point(4, 22); + this.symbolsTab.Name = "symbolsTab"; + this.symbolsTab.Padding = new System.Windows.Forms.Padding(3); + this.symbolsTab.Size = new System.Drawing.Size(614, 292); + this.symbolsTab.TabIndex = 1; + this.symbolsTab.Text = "Project Symbols"; + this.symbolsTab.UseVisualStyleBackColor = true; + // + // importSymbolsButton + // + this.importSymbolsButton.Location = new System.Drawing.Point(506, 158); + this.importSymbolsButton.Name = "importSymbolsButton"; + this.importSymbolsButton.Size = new System.Drawing.Size(102, 23); + this.importSymbolsButton.TabIndex = 5; + this.importSymbolsButton.Text = "&Import..."; + this.importSymbolsButton.UseVisualStyleBackColor = true; + this.importSymbolsButton.Click += new System.EventHandler(this.importSymbolsButton_Click); + // + // editSymbolButton + // + this.editSymbolButton.Location = new System.Drawing.Point(506, 52); + this.editSymbolButton.Name = "editSymbolButton"; + this.editSymbolButton.Size = new System.Drawing.Size(102, 23); + this.editSymbolButton.TabIndex = 3; + this.editSymbolButton.Text = "&Edit Symbol..."; + this.editSymbolButton.UseVisualStyleBackColor = true; + this.editSymbolButton.Click += new System.EventHandler(this.editSymbolButton_Click); + // + // removeSymbolButton + // + this.removeSymbolButton.Location = new System.Drawing.Point(506, 81); + this.removeSymbolButton.Name = "removeSymbolButton"; + this.removeSymbolButton.Size = new System.Drawing.Size(102, 23); + this.removeSymbolButton.TabIndex = 4; + this.removeSymbolButton.Text = "&Remove"; + this.removeSymbolButton.UseVisualStyleBackColor = true; + this.removeSymbolButton.Click += new System.EventHandler(this.removeSymbolButton_Click); + // + // newSymbolButton + // + this.newSymbolButton.Location = new System.Drawing.Point(506, 23); + this.newSymbolButton.Name = "newSymbolButton"; + this.newSymbolButton.Size = new System.Drawing.Size(102, 23); + this.newSymbolButton.TabIndex = 2; + this.newSymbolButton.Text = "&New Symbol..."; + this.newSymbolButton.UseVisualStyleBackColor = true; + this.newSymbolButton.Click += new System.EventHandler(this.newSymbolButton_Click); + // + // symbolsDefinedLabel + // + this.symbolsDefinedLabel.AutoSize = true; + this.symbolsDefinedLabel.Location = new System.Drawing.Point(7, 7); + this.symbolsDefinedLabel.Name = "symbolsDefinedLabel"; + this.symbolsDefinedLabel.Size = new System.Drawing.Size(133, 13); + this.symbolsDefinedLabel.TabIndex = 0; + this.symbolsDefinedLabel.Text = "Symbols defined in project:"; + // + // projectSymbolsListView + // + this.projectSymbolsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.nameColumnHeader, + this.valueColumnHeader, + this.typeColumnHeader, + this.commentColumnHeader}); + this.projectSymbolsListView.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.projectSymbolsListView.FullRowSelect = true; + this.projectSymbolsListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.projectSymbolsListView.HideSelection = false; + this.projectSymbolsListView.Items.AddRange(new System.Windows.Forms.ListViewItem[] { + listViewItem1}); + this.projectSymbolsListView.Location = new System.Drawing.Point(11, 23); + this.projectSymbolsListView.MultiSelect = false; + this.projectSymbolsListView.Name = "projectSymbolsListView"; + this.projectSymbolsListView.Size = new System.Drawing.Size(489, 259); + this.projectSymbolsListView.TabIndex = 1; + this.projectSymbolsListView.UseCompatibleStateImageBehavior = false; + this.projectSymbolsListView.View = System.Windows.Forms.View.Details; + this.projectSymbolsListView.SelectedIndexChanged += new System.EventHandler(this.projectSymbolsListView_SelectedIndexChanged); + this.projectSymbolsListView.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.projectSymbolsListView_MouseDoubleClick); + // + // nameColumnHeader + // + this.nameColumnHeader.Text = "Name"; + this.nameColumnHeader.Width = 109; + // + // valueColumnHeader + // + this.valueColumnHeader.Text = "Value"; + this.valueColumnHeader.Width = 69; + // + // typeColumnHeader + // + this.typeColumnHeader.Text = "Type"; + this.typeColumnHeader.Width = 42; + // + // commentColumnHeader + // + this.commentColumnHeader.Text = "Comment"; + this.commentColumnHeader.Width = 264; + // + // symbolFilesTab + // + this.symbolFilesTab.Controls.Add(this.symbolFileDownButton); + this.symbolFilesTab.Controls.Add(this.symbolFileUpButton); + this.symbolFilesTab.Controls.Add(this.addSymbolFilesButton); + this.symbolFilesTab.Controls.Add(this.symbolFileRemoveButton); + this.symbolFilesTab.Controls.Add(this.symbolFilesListBox); + this.symbolFilesTab.Controls.Add(this.configuredFilesLabel); + this.symbolFilesTab.Location = new System.Drawing.Point(4, 22); + this.symbolFilesTab.Name = "symbolFilesTab"; + this.symbolFilesTab.Padding = new System.Windows.Forms.Padding(3); + this.symbolFilesTab.Size = new System.Drawing.Size(614, 292); + this.symbolFilesTab.TabIndex = 2; + this.symbolFilesTab.Text = "Symbol Files"; + this.symbolFilesTab.UseVisualStyleBackColor = true; + // + // symbolFileDownButton + // + this.symbolFileDownButton.Location = new System.Drawing.Point(319, 57); + this.symbolFileDownButton.Name = "symbolFileDownButton"; + this.symbolFileDownButton.Size = new System.Drawing.Size(75, 23); + this.symbolFileDownButton.TabIndex = 4; + this.symbolFileDownButton.Text = "Down"; + this.symbolFileDownButton.UseVisualStyleBackColor = true; + this.symbolFileDownButton.Click += new System.EventHandler(this.symbolFileDownButton_Click); + // + // symbolFileUpButton + // + this.symbolFileUpButton.Location = new System.Drawing.Point(319, 28); + this.symbolFileUpButton.Name = "symbolFileUpButton"; + this.symbolFileUpButton.Size = new System.Drawing.Size(75, 23); + this.symbolFileUpButton.TabIndex = 3; + this.symbolFileUpButton.Text = "Up"; + this.symbolFileUpButton.UseVisualStyleBackColor = true; + this.symbolFileUpButton.Click += new System.EventHandler(this.symbolFileUpButton_Click); + // + // addSymbolFilesButton + // + this.addSymbolFilesButton.Location = new System.Drawing.Point(8, 249); + this.addSymbolFilesButton.Name = "addSymbolFilesButton"; + this.addSymbolFilesButton.Size = new System.Drawing.Size(134, 23); + this.addSymbolFilesButton.TabIndex = 2; + this.addSymbolFilesButton.Text = "Add Symbol Files..."; + this.addSymbolFilesButton.UseVisualStyleBackColor = true; + this.addSymbolFilesButton.Click += new System.EventHandler(this.addSymbolFilesButton_Click); + // + // symbolFileRemoveButton + // + this.symbolFileRemoveButton.Location = new System.Drawing.Point(319, 97); + this.symbolFileRemoveButton.Name = "symbolFileRemoveButton"; + this.symbolFileRemoveButton.Size = new System.Drawing.Size(75, 23); + this.symbolFileRemoveButton.TabIndex = 5; + this.symbolFileRemoveButton.Text = "Remove"; + this.symbolFileRemoveButton.UseVisualStyleBackColor = true; + this.symbolFileRemoveButton.Click += new System.EventHandler(this.symbolFileRemoveButton_Click); + // + // symbolFilesListBox + // + this.symbolFilesListBox.FormattingEnabled = true; + this.symbolFilesListBox.Location = new System.Drawing.Point(8, 28); + this.symbolFilesListBox.Name = "symbolFilesListBox"; + this.symbolFilesListBox.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended; + this.symbolFilesListBox.Size = new System.Drawing.Size(305, 212); + this.symbolFilesListBox.TabIndex = 1; + this.symbolFilesListBox.SelectedIndexChanged += new System.EventHandler(this.symbolFilesListBox_SelectedIndexChanged); + // + // configuredFilesLabel + // + this.configuredFilesLabel.AutoSize = true; + this.configuredFilesLabel.Location = new System.Drawing.Point(7, 7); + this.configuredFilesLabel.Name = "configuredFilesLabel"; + this.configuredFilesLabel.Size = new System.Drawing.Size(160, 13); + this.configuredFilesLabel.TabIndex = 0; + this.configuredFilesLabel.Text = "Currently configured symbol files:"; + // + // extensionScriptsTab + // + this.extensionScriptsTab.Controls.Add(this.extensionScriptRemoveButton); + this.extensionScriptsTab.Controls.Add(this.addExtensionScriptsButton); + this.extensionScriptsTab.Controls.Add(this.extensionScriptsListBox); + this.extensionScriptsTab.Controls.Add(this.configuredScriptsLabel); + this.extensionScriptsTab.Location = new System.Drawing.Point(4, 22); + this.extensionScriptsTab.Name = "extensionScriptsTab"; + this.extensionScriptsTab.Padding = new System.Windows.Forms.Padding(3); + this.extensionScriptsTab.Size = new System.Drawing.Size(614, 292); + this.extensionScriptsTab.TabIndex = 3; + this.extensionScriptsTab.Text = "Extension Scripts"; + this.extensionScriptsTab.UseVisualStyleBackColor = true; + // + // extensionScriptRemoveButton + // + this.extensionScriptRemoveButton.Location = new System.Drawing.Point(320, 28); + this.extensionScriptRemoveButton.Name = "extensionScriptRemoveButton"; + this.extensionScriptRemoveButton.Size = new System.Drawing.Size(75, 23); + this.extensionScriptRemoveButton.TabIndex = 3; + this.extensionScriptRemoveButton.Text = "Remove"; + this.extensionScriptRemoveButton.UseVisualStyleBackColor = true; + this.extensionScriptRemoveButton.Click += new System.EventHandler(this.extensionScriptRemoveButton_Click); + // + // addExtensionScriptsButton + // + this.addExtensionScriptsButton.Location = new System.Drawing.Point(8, 249); + this.addExtensionScriptsButton.Name = "addExtensionScriptsButton"; + this.addExtensionScriptsButton.Size = new System.Drawing.Size(134, 23); + this.addExtensionScriptsButton.TabIndex = 2; + this.addExtensionScriptsButton.Text = "Add Scripts..."; + this.addExtensionScriptsButton.UseVisualStyleBackColor = true; + this.addExtensionScriptsButton.Click += new System.EventHandler(this.addExtensionScriptsButton_Click); + // + // extensionScriptsListBox + // + this.extensionScriptsListBox.FormattingEnabled = true; + this.extensionScriptsListBox.Location = new System.Drawing.Point(8, 28); + this.extensionScriptsListBox.Name = "extensionScriptsListBox"; + this.extensionScriptsListBox.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended; + this.extensionScriptsListBox.Size = new System.Drawing.Size(305, 212); + this.extensionScriptsListBox.TabIndex = 1; + this.extensionScriptsListBox.SelectedIndexChanged += new System.EventHandler(this.extensionScriptsListBox_SelectedIndexChanged); + // + // configuredScriptsLabel + // + this.configuredScriptsLabel.AutoSize = true; + this.configuredScriptsLabel.Location = new System.Drawing.Point(7, 7); + this.configuredScriptsLabel.Name = "configuredScriptsLabel"; + this.configuredScriptsLabel.Size = new System.Drawing.Size(185, 13); + this.configuredScriptsLabel.TabIndex = 0; + this.configuredScriptsLabel.Text = "Currently configured extension scripts:"; + // + // labelUndoRedoNote + // + this.labelUndoRedoNote.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.labelUndoRedoNote.AutoSize = true; + this.labelUndoRedoNote.Location = new System.Drawing.Point(12, 331); + this.labelUndoRedoNote.Name = "labelUndoRedoNote"; + this.labelUndoRedoNote.Size = new System.Drawing.Size(248, 13); + this.labelUndoRedoNote.TabIndex = 1; + this.labelUndoRedoNote.Text = "NOTE: changes are added to the undo/redo buffer"; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(537, 326); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 4; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(456, 326); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 3; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // applyButton + // + this.applyButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.applyButton.Location = new System.Drawing.Point(354, 326); + this.applyButton.Name = "applyButton"; + this.applyButton.Size = new System.Drawing.Size(75, 23); + this.applyButton.TabIndex = 2; + this.applyButton.Text = "Apply"; + this.applyButton.UseVisualStyleBackColor = true; + this.applyButton.Click += new System.EventHandler(this.applyButton_Click); + // + // EditProjectProperties + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(624, 361); + this.Controls.Add(this.applyButton); + this.Controls.Add(this.okButton); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.labelUndoRedoNote); + this.Controls.Add(this.tabControl1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditProjectProperties"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Project Properties"; + this.Load += new System.EventHandler(this.EditProperties_Load); + this.tabControl1.ResumeLayout(false); + this.generalTab.ResumeLayout(false); + this.analysisGroupBox.ResumeLayout(false); + this.analysisGroupBox.PerformLayout(); + this.entryFlagsGroupBox.ResumeLayout(false); + this.entryFlagsGroupBox.PerformLayout(); + this.cpuGroupBox.ResumeLayout(false); + this.cpuGroupBox.PerformLayout(); + this.symbolsTab.ResumeLayout(false); + this.symbolsTab.PerformLayout(); + this.symbolFilesTab.ResumeLayout(false); + this.symbolFilesTab.PerformLayout(); + this.extensionScriptsTab.ResumeLayout(false); + this.extensionScriptsTab.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage generalTab; + private System.Windows.Forms.ComboBox cpuComboBox; + private System.Windows.Forms.TabPage symbolsTab; + private System.Windows.Forms.Label labelUndoRedoNote; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Button applyButton; + private System.Windows.Forms.Label currentFlagsLabel; + private System.Windows.Forms.Button changeFlagButton; + private System.Windows.Forms.Label symbolsDefinedLabel; + private System.Windows.Forms.ListView projectSymbolsListView; + private System.Windows.Forms.ColumnHeader nameColumnHeader; + private System.Windows.Forms.ColumnHeader valueColumnHeader; + private System.Windows.Forms.ColumnHeader typeColumnHeader; + private System.Windows.Forms.TabPage symbolFilesTab; + private System.Windows.Forms.Button addSymbolFilesButton; + private System.Windows.Forms.Button symbolFileRemoveButton; + private System.Windows.Forms.ListBox symbolFilesListBox; + private System.Windows.Forms.Label configuredFilesLabel; + private System.Windows.Forms.Button removeSymbolButton; + private System.Windows.Forms.Button newSymbolButton; + private System.Windows.Forms.ColumnHeader commentColumnHeader; + private System.Windows.Forms.GroupBox entryFlagsGroupBox; + private System.Windows.Forms.Label flagsLabel; + private System.Windows.Forms.GroupBox cpuGroupBox; + private System.Windows.Forms.CheckBox undocInstrCheckBox; + private System.Windows.Forms.Button editSymbolButton; + private System.Windows.Forms.Button symbolFileDownButton; + private System.Windows.Forms.Button symbolFileUpButton; + private System.Windows.Forms.GroupBox analysisGroupBox; + private System.Windows.Forms.ComboBox minStringCharsComboBox; + private System.Windows.Forms.Label minCharsForStringLabel; + private System.Windows.Forms.CheckBox analyzeUncategorizedCheckBox; + private System.Windows.Forms.TabPage extensionScriptsTab; + private System.Windows.Forms.Button extensionScriptRemoveButton; + private System.Windows.Forms.Button addExtensionScriptsButton; + private System.Windows.Forms.ListBox extensionScriptsListBox; + private System.Windows.Forms.Label configuredScriptsLabel; + private System.Windows.Forms.Button importSymbolsButton; + private System.Windows.Forms.CheckBox seekAltTargetCheckBox; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/EditProjectProperties.cs b/SourceGen/AppForms/EditProjectProperties.cs new file mode 100644 index 0000000..ff59a56 --- /dev/null +++ b/SourceGen/AppForms/EditProjectProperties.cs @@ -0,0 +1,631 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.IO; +using System.Text; +using System.Windows.Forms; + +using Asm65; +using CommonUtil; + +namespace SourceGen.AppForms { + /// + /// Edit project properties. + /// + /// Changes are made locally, and pushed to NewProps when OK or Apply is clicked. When + /// the dialog exits, if NewProps is non-null, the caller should apply those changes + /// regardless of the dialog's return value. + /// + public partial class EditProjectProperties : Form { + /// + /// Working set. Used internally to hold state. + /// + private ProjectProperties WorkProps { get; set; } + + /// + /// New set. Updated when Apply or OK is hit. This will be null if no changes have + /// been applied. + /// + public ProjectProperties NewProps { get; private set; } + + /// + /// Format object to use when formatting addresses and constants. + /// + public Formatter NumFormatter { get; set; } + + /// + /// Dirty flag. Ideally this would just be "WorkProps != OldProps", but it doesn't + /// seem worthwhile to maintain an equality operator. + /// + private bool mDirty; + + /// + /// Project directory, if one has been established; otherwise empty. + /// + private string mProjectDir; + + + public EditProjectProperties(string projectDir) { + InitializeComponent(); + + mProjectDir = projectDir; + } + + /// + /// Sets the initial state from an existing ProjectProperties object. This must be + /// called, and must be called before the dialog is shown. + /// + /// Object to clone. + public void SetInitialProps(ProjectProperties props) { + WorkProps = new ProjectProperties(props); + } + + private void EditProperties_Load(object sender, EventArgs e) { + // Configure CPU chooser. This must match the order of strings in the designer. + switch (WorkProps.CpuType) { + case CpuDef.CpuType.Cpu6502: + cpuComboBox.SelectedIndex = 0; + break; + case CpuDef.CpuType.Cpu65C02: + cpuComboBox.SelectedIndex = 1; + break; + case CpuDef.CpuType.Cpu65816: + cpuComboBox.SelectedIndex = 2; + break; + default: + Debug.Assert(false); + cpuComboBox.SelectedIndex = 0; + break; + } + + undocInstrCheckBox.Checked = WorkProps.IncludeUndocumentedInstr; + analyzeUncategorizedCheckBox.Checked = + WorkProps.AnalysisParams.AnalyzeUncategorizedData; + seekAltTargetCheckBox.Checked = + WorkProps.AnalysisParams.SeekNearbyTargets; + + int matchLen = WorkProps.AnalysisParams.MinCharsForString; + int selIndex; + if (matchLen == DataAnalysis.MIN_CHARS_FOR_STRING_DISABLED) { + selIndex = 0; // disabled + } else { + selIndex = matchLen - 2; + } + if (selIndex < 0 || selIndex >= minStringCharsComboBox.Items.Count) { + Debug.Assert(false, "bad MinCharsForString " + matchLen); + selIndex = 0; + } + minStringCharsComboBox.SelectedIndex = selIndex; + + LoadProjectSymbols(); + LoadPlatformSymbolFiles(); + LoadExtensionScriptNames(); + + // Various callbacks will have fired while configuring controls. Reset to "clean". + mDirty = false; + UpdateControls(); + } + + private void okButton_Click(object sender, EventArgs e) { + NewProps = new ProjectProperties(WorkProps); + } + + private void applyButton_Click(object sender, EventArgs e) { + NewProps = new ProjectProperties(WorkProps); + mDirty = false; + UpdateControls(); + } + + private void UpdateControls() { + // + // General tab + // + applyButton.Enabled = mDirty; + + const string FLAGS = "CZIDXMVNE"; // flags, in order low to high, plus emu bit + const string VALUES = "-?01"; + StringBuilder sb = new StringBuilder(27); + StatusFlags flags = WorkProps.EntryFlags; + for (int i = 0; i < 9; i++) { + // Want to show P reg flags (first 8) in conventional high-to-low order. + int idx = (7 - i) + (i == 8 ? 9 : 0); + int val = flags.GetBit((StatusFlags.FlagBits)idx); + sb.Append(FLAGS[idx]); + sb.Append(VALUES[val + 2]); + sb.Append(' '); + } + + currentFlagsLabel.Text = sb.ToString(); + + // + // Project symbols tab + // + int symSelCount = projectSymbolsListView.SelectedIndices.Count; + removeSymbolButton.Enabled = (symSelCount == 1); + editSymbolButton.Enabled = (symSelCount == 1); + + // + // Platform symbol files tab + // + int fileSelCount = symbolFilesListBox.SelectedIndices.Count; + symbolFileRemoveButton.Enabled = (fileSelCount != 0); + symbolFileUpButton.Enabled = (fileSelCount == 1 && + symbolFilesListBox.SelectedIndices[0] != 0); + symbolFileDownButton.Enabled = (fileSelCount == 1 && + symbolFilesListBox.SelectedIndices[0] != symbolFilesListBox.Items.Count - 1); + + // + // Extension Scripts tab + // + fileSelCount = extensionScriptsListBox.SelectedIndices.Count; + extensionScriptRemoveButton.Enabled = (fileSelCount != 0); + } + + + #region General + + /// + /// Converts the CPU combo box selection to a CpuType enum value. + /// + /// Selection index. + /// CPU type. + private CpuDef.CpuType CpuSelectionToCpuType(int sel) { + switch (sel) { + case 0: return CpuDef.CpuType.Cpu6502; + case 1: return CpuDef.CpuType.Cpu65C02; + case 2: return CpuDef.CpuType.Cpu65816; + default: + Debug.Assert(false); + return CpuDef.CpuType.Cpu6502; + } + } + + private void cpuComboBox_SelectedIndexChanged(object sender, EventArgs e) { + CpuDef.CpuType cpuType = CpuSelectionToCpuType(cpuComboBox.SelectedIndex); + if (WorkProps.CpuType != cpuType) { + WorkProps.CpuType = cpuType; + mDirty = true; + UpdateControls(); + } + } + + private void undocInstrCheckBox_CheckedChanged(object sender, EventArgs e) { + if (WorkProps.IncludeUndocumentedInstr != undocInstrCheckBox.Checked) { + WorkProps.IncludeUndocumentedInstr = undocInstrCheckBox.Checked; + mDirty = true; + UpdateControls(); + } + } + + private void changeFlagButton_Click(object sender, EventArgs e) { + EditStatusFlags dlg = new EditStatusFlags(); + dlg.FlagValue = WorkProps.EntryFlags; + + CpuDef cpuDef = CpuDef.GetBestMatch(WorkProps.CpuType, + WorkProps.IncludeUndocumentedInstr); + dlg.HasEmuFlag = cpuDef.HasEmuFlag; + + dlg.ShowDialog(); + if (dlg.DialogResult == DialogResult.OK) { + if (WorkProps.EntryFlags != dlg.FlagValue) { + // Flags changed. + WorkProps.EntryFlags = dlg.FlagValue; + mDirty = true; + UpdateControls(); + } + } + + dlg.Dispose(); + } + + private void analyzeUncategorizedCheckBox_CheckedChanged(object sender, EventArgs e) { + WorkProps.AnalysisParams.AnalyzeUncategorizedData = + analyzeUncategorizedCheckBox.Checked; + mDirty = true; + UpdateControls(); + } + + private void seekAltTargetCheckBox_CheckedChanged(object sender, EventArgs e) { + WorkProps.AnalysisParams.SeekNearbyTargets = + seekAltTargetCheckBox.Checked; + mDirty = true; + UpdateControls(); + } + + private void minStringCharsComboBox_SelectedIndexChanged(object sender, EventArgs e) { + int index = minStringCharsComboBox.SelectedIndex; + int newVal; + if (index == 0) { + newVal = DataAnalysis.MIN_CHARS_FOR_STRING_DISABLED; + } else { + newVal = index + 2; + } + + if (newVal != WorkProps.AnalysisParams.MinCharsForString) { + WorkProps.AnalysisParams.MinCharsForString = newVal; + mDirty = true; + UpdateControls(); + } + } + + #endregion General + + + #region Project Symbols + + private ListViewItem.ListViewSubItem[] mSymbolSubArray = + new ListViewItem.ListViewSubItem[3]; + + /// + /// Loads the project symbols into the ListView. + /// + private void LoadProjectSymbols() { + // The set should be small enough that we don't need to worry about updating + // the item list incrementally. + //Debug.WriteLine("LPS loading " + WorkProps.ProjectSyms.Count + " project symbols"); + projectSymbolsListView.BeginUpdate(); + projectSymbolsListView.Items.Clear(); + foreach (KeyValuePair kvp in WorkProps.ProjectSyms) { + DefSymbol defSym = kvp.Value; + string typeStr; + if (defSym.SymbolType == Symbol.Type.Constant) { + typeStr = Properties.Resources.ABBREV_CONSTANT; + } else { + typeStr = Properties.Resources.ABBREV_ADDRESS; + } + + ListViewItem lvi = new ListViewItem(); + lvi.Text = defSym.Label; + mSymbolSubArray[0] = new ListViewItem.ListViewSubItem(lvi, + NumFormatter.FormatValueInBase(defSym.Value, defSym.DataDescriptor.NumBase)); + mSymbolSubArray[1] = new ListViewItem.ListViewSubItem(lvi, typeStr); + mSymbolSubArray[2] = new ListViewItem.ListViewSubItem(lvi, defSym.Comment); + lvi.SubItems.AddRange(mSymbolSubArray); + + projectSymbolsListView.Items.Add(lvi); + } + projectSymbolsListView.EndUpdate(); + } + + + private void projectSymbolsListView_SelectedIndexChanged(object sender, EventArgs e) { + // Need to enable/disable the edit+remove buttons depending on the number of + // selected items. + UpdateControls(); + } + + private void newSymbolButton_Click(object sender, EventArgs e) { + EditDefSymbol dlg = new EditDefSymbol(NumFormatter, WorkProps.ProjectSyms); + dlg.ShowDialog(); + if (dlg.DialogResult == DialogResult.OK) { + Debug.WriteLine("ADD: " + dlg.DefSym); + WorkProps.ProjectSyms[dlg.DefSym.Label] = dlg.DefSym; + mDirty = true; + LoadProjectSymbols(); + UpdateControls(); + } + dlg.Dispose(); + } + + private void editSymbolButton_Click(object sender, EventArgs e) { + // Single-select list view, button dimmed when no selection. + Debug.Assert(projectSymbolsListView.SelectedItems.Count == 1); + ListViewItem item = projectSymbolsListView.SelectedItems[0]; + DefSymbol defSym = WorkProps.ProjectSyms[item.Text]; + DoEditSymbol(defSym); + } + + private void projectSymbolsListView_MouseDoubleClick(object sender, MouseEventArgs e) { + ListViewHitTestInfo info = projectSymbolsListView.HitTest(e.X, e.Y); + DefSymbol defSym = WorkProps.ProjectSyms[info.Item.Text]; + DoEditSymbol(defSym); + } + + private void DoEditSymbol(DefSymbol defSym) { + EditDefSymbol dlg = new EditDefSymbol(NumFormatter, WorkProps.ProjectSyms); + dlg.DefSym = defSym; + dlg.ShowDialog(); + if (dlg.DialogResult == DialogResult.OK) { + // Label might have changed, so remove old before adding new. + WorkProps.ProjectSyms.Remove(defSym.Label); + WorkProps.ProjectSyms[dlg.DefSym.Label] = dlg.DefSym; + mDirty = true; + LoadProjectSymbols(); + UpdateControls(); + } + dlg.Dispose(); + } + + private void removeSymbolButton_Click(object sender, EventArgs e) { + // Single-select list view, button dimmed when no selection. + Debug.Assert(projectSymbolsListView.SelectedItems.Count == 1); + + int selectionIndex = projectSymbolsListView.SelectedIndices[0]; + ListViewItem item = projectSymbolsListView.SelectedItems[0]; + DefSymbol defSym = WorkProps.ProjectSyms[item.Text]; + WorkProps.ProjectSyms.Remove(defSym.Label); + mDirty = true; + LoadProjectSymbols(); + UpdateControls(); + + // Restore selection, so you can hit "Remove" repeatedly to delete + // multiple items. + int newCount = projectSymbolsListView.Items.Count; + if (selectionIndex >= newCount) { + selectionIndex = newCount - 1; + } + if (selectionIndex >= 0) { + projectSymbolsListView.SelectedIndices.Add(selectionIndex); + removeSymbolButton.Focus(); + } + } + + #endregion Project Symbols + + + #region Platform symbol files + + /// + /// Loads the platform symbol file names into the list control. + /// + private void LoadPlatformSymbolFiles() { + symbolFilesListBox.BeginUpdate(); + symbolFilesListBox.Items.Clear(); + + foreach (string fileName in WorkProps.PlatformSymbolFileIdentifiers) { + symbolFilesListBox.Items.Add(fileName); + } + + symbolFilesListBox.EndUpdate(); + } + + private void symbolFilesListBox_SelectedIndexChanged(object sender, EventArgs e) { + // Enable/disable buttons as the selection changes. + UpdateControls(); + } + + private void addSymbolFilesButton_Click(object sender, EventArgs e) { + OpenFileDialog fileDlg = new OpenFileDialog(); + fileDlg.Filter = PlatformSymbols.FILENAME_FILTER; + fileDlg.Multiselect = true; + fileDlg.InitialDirectory = RuntimeDataAccess.GetDirectory(); + fileDlg.RestoreDirectory = true; // doesn't seem to work? + if (fileDlg.ShowDialog() != DialogResult.OK) { + return; + } + + foreach (string pathName in fileDlg.FileNames) { + // I'm assuming the full names got the Path.GetFullPath() canonicalization and + // don't need further processing. Also, I'm assuming that all files live in + // the same directory, so if one is in an invalid location then they all are. + ExternalFile ef = ExternalFile.CreateFromPath(pathName, mProjectDir); + if (ef == null) { + // Files not found in runtime or project directory. + string projDir = mProjectDir; + if (string.IsNullOrEmpty(projDir)) { + projDir = Properties.Resources.UNSET; + } + string msg = string.Format(Properties.Resources.EXTERNAL_FILE_BAD_DIR, + RuntimeDataAccess.GetDirectory(), projDir, pathName); + MessageBox.Show(msg, Properties.Resources.EXTERNAL_FILE_BAD_DIR_CAPTION, + MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + string ident = ef.Identifier; + + if (WorkProps.PlatformSymbolFileIdentifiers.Contains(ident)) { + Debug.WriteLine("Already present: " + ident); + continue; + } + + Debug.WriteLine("Adding symbol file: " + ident); + WorkProps.PlatformSymbolFileIdentifiers.Add(ident); + mDirty = true; + } + + if (mDirty) { + LoadPlatformSymbolFiles(); + UpdateControls(); + } + } + + private void symbolFileUpButton_Click(object sender, EventArgs e) { + Debug.Assert(symbolFilesListBox.SelectedIndices.Count == 1); + int selIndex = symbolFilesListBox.SelectedIndices[0]; + Debug.Assert(selIndex > 0); + + MoveSingleItem(selIndex, symbolFilesListBox.SelectedItem, -1); + } + + private void symbolFileDownButton_Click(object sender, EventArgs e) { + Debug.Assert(symbolFilesListBox.SelectedIndices.Count == 1); + int selIndex = symbolFilesListBox.SelectedIndices[0]; + Debug.Assert(selIndex < symbolFilesListBox.Items.Count - 1); + + MoveSingleItem(selIndex, symbolFilesListBox.SelectedItem, +1); + } + + private void MoveSingleItem(int selIndex, object selectedItem, int adj) { + object selected = symbolFilesListBox.SelectedItem; + symbolFilesListBox.Items.Remove(selected); + symbolFilesListBox.Items.Insert(selIndex + adj, selected); + symbolFilesListBox.SetSelected(selIndex + adj, true); + + // do the same operation in the file name list + string str = WorkProps.PlatformSymbolFileIdentifiers[selIndex]; + WorkProps.PlatformSymbolFileIdentifiers.RemoveAt(selIndex); + WorkProps.PlatformSymbolFileIdentifiers.Insert(selIndex + adj, str); + + mDirty = true; + UpdateControls(); + } + + private void symbolFileRemoveButton_Click(object sender, EventArgs e) { + Debug.Assert(symbolFilesListBox.SelectedIndices.Count > 0); + for (int i = symbolFilesListBox.SelectedIndices.Count - 1; i >= 0; i--) { + int index = symbolFilesListBox.SelectedIndices[i]; + symbolFilesListBox.Items.RemoveAt(index); + WorkProps.PlatformSymbolFileIdentifiers.RemoveAt(index); + } + + mDirty = true; + UpdateControls(); + } + + private void importSymbolsButton_Click(object sender, EventArgs e) { + OpenFileDialog fileDlg = new OpenFileDialog(); + + fileDlg.Filter = ProjectFile.FILENAME_FILTER + "|" + + Properties.Resources.FILE_FILTER_ALL; + fileDlg.FilterIndex = 1; + if (fileDlg.ShowDialog() != DialogResult.OK) { + return; + } + string projPathName = Path.GetFullPath(fileDlg.FileName); + + DisasmProject newProject = new DisasmProject(); + if (!ProjectFile.DeserializeFromFile(projPathName, newProject, + out FileLoadReport report)) { + ProjectLoadIssues dlg = new ProjectLoadIssues(); + dlg.Messages = report.Format(); + dlg.CanContinue = false; + dlg.ShowDialog(); + // ignore dlg.DialogResult + dlg.Dispose(); + return; + } + + // Import all user labels that were marked as "global export". These become + // external-address project symbols. + int foundCount = 0; + foreach (KeyValuePair kvp in newProject.UserLabels) { + if (kvp.Value.SymbolType == Symbol.Type.GlobalAddrExport) { + Symbol sym = kvp.Value; + DefSymbol defSym = new DefSymbol(sym.Label, sym.Value, Symbol.Source.Project, + Symbol.Type.ExternalAddr, FormatDescriptor.SubType.None, + string.Empty, string.Empty); + WorkProps.ProjectSyms[defSym.Label] = defSym; + foundCount++; + } + } + if (foundCount != 0) { + mDirty = true; + LoadProjectSymbols(); + UpdateControls(); + } + + newProject.Cleanup(); + + // Tell the user we did something. Might be nice to tell them how many weren't + // already present. + string msg; + if (foundCount == 0) { + msg = Properties.Resources.SYMBOL_IMPORT_NONE; + } else { + msg = string.Format(Properties.Resources.SYMBOL_IMPORT_GOOD, foundCount); + } + MessageBox.Show(msg, Properties.Resources.SYMBOL_IMPORT_CAPTION, + MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + #endregion Platform symbol files + + + #region Extension scripts + + /// + /// Loads the extension script file names into the list control. + /// + private void LoadExtensionScriptNames() { + extensionScriptsListBox.BeginUpdate(); + extensionScriptsListBox.Items.Clear(); + + foreach (string fileName in WorkProps.ExtensionScriptFileIdentifiers) { + extensionScriptsListBox.Items.Add(fileName); + } + + extensionScriptsListBox.EndUpdate(); + } + + private void extensionScriptsListBox_SelectedIndexChanged(object sender, EventArgs e) { + // Enable/disable buttons as the selection changes. + UpdateControls(); + } + + private void addExtensionScriptsButton_Click(object sender, EventArgs e) { + OpenFileDialog fileDlg = new OpenFileDialog(); + fileDlg.Filter = Sandbox.ScriptManager.FILENAME_FILTER; + fileDlg.Multiselect = true; + fileDlg.InitialDirectory = RuntimeDataAccess.GetDirectory(); + fileDlg.RestoreDirectory = true; // doesn't seem to work? + if (fileDlg.ShowDialog() != DialogResult.OK) { + return; + } + + foreach (string pathName in fileDlg.FileNames) { + // I'm assuming the full names got the Path.GetFullPath() canonicalization and + // don't need further processing. Also, I'm assuming that all files live in + // the same directory, so if one is in an invalid location then they all are. + ExternalFile ef = ExternalFile.CreateFromPath(pathName, mProjectDir); + if (ef == null) { + // Files not found in runtime or project directory. + string projDir = mProjectDir; + if (string.IsNullOrEmpty(projDir)) { + projDir = Properties.Resources.UNSET; + } + string msg = string.Format(Properties.Resources.EXTERNAL_FILE_BAD_DIR, + RuntimeDataAccess.GetDirectory(), projDir, pathName); + MessageBox.Show(msg, Properties.Resources.EXTERNAL_FILE_BAD_DIR_CAPTION, + MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + string ident = ef.Identifier; + + if (WorkProps.ExtensionScriptFileIdentifiers.Contains(ident)) { + Debug.WriteLine("Already present: " + ident); + continue; + } + + Debug.WriteLine("Adding extension script: " + ident); + WorkProps.ExtensionScriptFileIdentifiers.Add(ident); + mDirty = true; + } + + if (mDirty) { + LoadExtensionScriptNames(); + UpdateControls(); + } + } + + private void extensionScriptRemoveButton_Click(object sender, EventArgs e) { + Debug.Assert(extensionScriptsListBox.SelectedIndices.Count > 0); + for (int i = extensionScriptsListBox.SelectedIndices.Count - 1; i >= 0; i--) { + int index = extensionScriptsListBox.SelectedIndices[i]; + extensionScriptsListBox.Items.RemoveAt(index); + WorkProps.ExtensionScriptFileIdentifiers.RemoveAt(index); + } + + mDirty = true; + UpdateControls(); + } + + #endregion Extension scripts + } +} diff --git a/SourceGen/AppForms/EditProjectProperties.resx b/SourceGen/AppForms/EditProjectProperties.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/EditProjectProperties.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/EditStatusFlags.Designer.cs b/SourceGen/AppForms/EditStatusFlags.Designer.cs new file mode 100644 index 0000000..b7a26e1 --- /dev/null +++ b/SourceGen/AppForms/EditStatusFlags.Designer.cs @@ -0,0 +1,883 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class EditStatusFlags { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.defaultLabel = new System.Windows.Forms.Label(); + this.zeroLabel = new System.Windows.Forms.Label(); + this.oneLabel = new System.Windows.Forms.Label(); + this.indeterminateLabel = new System.Windows.Forms.Label(); + this.panelN = new System.Windows.Forms.Panel(); + this.flagNLabel = new System.Windows.Forms.Label(); + this.radioNIndeterminate = new System.Windows.Forms.RadioButton(); + this.radioNOne = new System.Windows.Forms.RadioButton(); + this.radioNZero = new System.Windows.Forms.RadioButton(); + this.radioNDefault = new System.Windows.Forms.RadioButton(); + this.panelV = new System.Windows.Forms.Panel(); + this.flagVLabel = new System.Windows.Forms.Label(); + this.radioVIndeterminate = new System.Windows.Forms.RadioButton(); + this.radioVOne = new System.Windows.Forms.RadioButton(); + this.radioVZero = new System.Windows.Forms.RadioButton(); + this.radioVDefault = new System.Windows.Forms.RadioButton(); + this.okButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.panelM = new System.Windows.Forms.Panel(); + this.flagMLabel = new System.Windows.Forms.Label(); + this.radioMIndeterminate = new System.Windows.Forms.RadioButton(); + this.radioMOne = new System.Windows.Forms.RadioButton(); + this.radioMZero = new System.Windows.Forms.RadioButton(); + this.radioMDefault = new System.Windows.Forms.RadioButton(); + this.panelX = new System.Windows.Forms.Panel(); + this.flagXLabel = new System.Windows.Forms.Label(); + this.radioXIndeterminate = new System.Windows.Forms.RadioButton(); + this.radioXOne = new System.Windows.Forms.RadioButton(); + this.radioXZero = new System.Windows.Forms.RadioButton(); + this.radioXDefault = new System.Windows.Forms.RadioButton(); + this.panelD = new System.Windows.Forms.Panel(); + this.flagDLabel = new System.Windows.Forms.Label(); + this.radioDIndeterminate = new System.Windows.Forms.RadioButton(); + this.radioDOne = new System.Windows.Forms.RadioButton(); + this.radioDZero = new System.Windows.Forms.RadioButton(); + this.radioDDefault = new System.Windows.Forms.RadioButton(); + this.panelI = new System.Windows.Forms.Panel(); + this.flagILabel = new System.Windows.Forms.Label(); + this.radioIIndeterminate = new System.Windows.Forms.RadioButton(); + this.radioIOne = new System.Windows.Forms.RadioButton(); + this.radioIZero = new System.Windows.Forms.RadioButton(); + this.radioIDefault = new System.Windows.Forms.RadioButton(); + this.panelZ = new System.Windows.Forms.Panel(); + this.flagZLabel = new System.Windows.Forms.Label(); + this.radioZIndeterminate = new System.Windows.Forms.RadioButton(); + this.radioZOne = new System.Windows.Forms.RadioButton(); + this.radioZZero = new System.Windows.Forms.RadioButton(); + this.radioZDefault = new System.Windows.Forms.RadioButton(); + this.panelC = new System.Windows.Forms.Panel(); + this.flagCLabel = new System.Windows.Forms.Label(); + this.radioCIndeterminate = new System.Windows.Forms.RadioButton(); + this.radioCOne = new System.Windows.Forms.RadioButton(); + this.radioCZero = new System.Windows.Forms.RadioButton(); + this.radioCDefault = new System.Windows.Forms.RadioButton(); + this.panelE = new System.Windows.Forms.Panel(); + this.flagELabel = new System.Windows.Forms.Label(); + this.radioEIndeterminate = new System.Windows.Forms.RadioButton(); + this.radioEOne = new System.Windows.Forms.RadioButton(); + this.radioEZero = new System.Windows.Forms.RadioButton(); + this.radioEDefault = new System.Windows.Forms.RadioButton(); + this.instructionLabel = new System.Windows.Forms.Label(); + this.labelLongRegsNote = new System.Windows.Forms.Label(); + this.resetButton = new System.Windows.Forms.Button(); + this.panelN.SuspendLayout(); + this.panelV.SuspendLayout(); + this.panelM.SuspendLayout(); + this.panelX.SuspendLayout(); + this.panelD.SuspendLayout(); + this.panelI.SuspendLayout(); + this.panelZ.SuspendLayout(); + this.panelC.SuspendLayout(); + this.panelE.SuspendLayout(); + this.SuspendLayout(); + // + // defaultLabel + // + this.defaultLabel.Location = new System.Drawing.Point(12, 70); + this.defaultLabel.Name = "defaultLabel"; + this.defaultLabel.Size = new System.Drawing.Size(90, 20); + this.defaultLabel.TabIndex = 4; + this.defaultLabel.Text = "Default"; + this.defaultLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // zeroLabel + // + this.zeroLabel.Location = new System.Drawing.Point(12, 90); + this.zeroLabel.Name = "zeroLabel"; + this.zeroLabel.Size = new System.Drawing.Size(90, 20); + this.zeroLabel.TabIndex = 5; + this.zeroLabel.Text = "Zero"; + this.zeroLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // oneLabel + // + this.oneLabel.Location = new System.Drawing.Point(12, 110); + this.oneLabel.Name = "oneLabel"; + this.oneLabel.Size = new System.Drawing.Size(90, 20); + this.oneLabel.TabIndex = 6; + this.oneLabel.Text = "One"; + this.oneLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // indeterminateLabel + // + this.indeterminateLabel.Location = new System.Drawing.Point(12, 130); + this.indeterminateLabel.Name = "indeterminateLabel"; + this.indeterminateLabel.Size = new System.Drawing.Size(90, 20); + this.indeterminateLabel.TabIndex = 7; + this.indeterminateLabel.Text = "Indeterminate"; + this.indeterminateLabel.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // panelN + // + this.panelN.Controls.Add(this.flagNLabel); + this.panelN.Controls.Add(this.radioNIndeterminate); + this.panelN.Controls.Add(this.radioNOne); + this.panelN.Controls.Add(this.radioNZero); + this.panelN.Controls.Add(this.radioNDefault); + this.panelN.Location = new System.Drawing.Point(108, 47); + this.panelN.Name = "panelN"; + this.panelN.Size = new System.Drawing.Size(26, 110); + this.panelN.TabIndex = 9; + // + // flagNLabel + // + this.flagNLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.flagNLabel.Location = new System.Drawing.Point(3, 0); + this.flagNLabel.Name = "flagNLabel"; + this.flagNLabel.Size = new System.Drawing.Size(20, 20); + this.flagNLabel.TabIndex = 0; + this.flagNLabel.Text = "N"; + this.flagNLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // radioNIndeterminate + // + this.radioNIndeterminate.AutoSize = true; + this.radioNIndeterminate.Location = new System.Drawing.Point(6, 83); + this.radioNIndeterminate.Name = "radioNIndeterminate"; + this.radioNIndeterminate.Size = new System.Drawing.Size(14, 13); + this.radioNIndeterminate.TabIndex = 4; + this.radioNIndeterminate.TabStop = true; + this.radioNIndeterminate.UseVisualStyleBackColor = true; + // + // radioNOne + // + this.radioNOne.AutoSize = true; + this.radioNOne.Location = new System.Drawing.Point(6, 63); + this.radioNOne.Name = "radioNOne"; + this.radioNOne.Size = new System.Drawing.Size(14, 13); + this.radioNOne.TabIndex = 3; + this.radioNOne.TabStop = true; + this.radioNOne.UseVisualStyleBackColor = true; + // + // radioNZero + // + this.radioNZero.AutoSize = true; + this.radioNZero.Location = new System.Drawing.Point(6, 43); + this.radioNZero.Name = "radioNZero"; + this.radioNZero.Size = new System.Drawing.Size(14, 13); + this.radioNZero.TabIndex = 2; + this.radioNZero.TabStop = true; + this.radioNZero.UseVisualStyleBackColor = true; + // + // radioNDefault + // + this.radioNDefault.AutoSize = true; + this.radioNDefault.Location = new System.Drawing.Point(6, 23); + this.radioNDefault.Name = "radioNDefault"; + this.radioNDefault.Size = new System.Drawing.Size(14, 13); + this.radioNDefault.TabIndex = 1; + this.radioNDefault.TabStop = true; + this.radioNDefault.UseVisualStyleBackColor = true; + // + // panelV + // + this.panelV.Controls.Add(this.flagVLabel); + this.panelV.Controls.Add(this.radioVIndeterminate); + this.panelV.Controls.Add(this.radioVOne); + this.panelV.Controls.Add(this.radioVZero); + this.panelV.Controls.Add(this.radioVDefault); + this.panelV.Location = new System.Drawing.Point(140, 47); + this.panelV.Name = "panelV"; + this.panelV.Size = new System.Drawing.Size(26, 110); + this.panelV.TabIndex = 14; + // + // flagVLabel + // + this.flagVLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.flagVLabel.Location = new System.Drawing.Point(3, 0); + this.flagVLabel.Name = "flagVLabel"; + this.flagVLabel.Size = new System.Drawing.Size(20, 20); + this.flagVLabel.TabIndex = 0; + this.flagVLabel.Text = "V"; + this.flagVLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // radioVIndeterminate + // + this.radioVIndeterminate.AutoSize = true; + this.radioVIndeterminate.Location = new System.Drawing.Point(6, 83); + this.radioVIndeterminate.Name = "radioVIndeterminate"; + this.radioVIndeterminate.Size = new System.Drawing.Size(14, 13); + this.radioVIndeterminate.TabIndex = 4; + this.radioVIndeterminate.TabStop = true; + this.radioVIndeterminate.UseVisualStyleBackColor = true; + // + // radioVOne + // + this.radioVOne.AutoSize = true; + this.radioVOne.Location = new System.Drawing.Point(6, 63); + this.radioVOne.Name = "radioVOne"; + this.radioVOne.Size = new System.Drawing.Size(14, 13); + this.radioVOne.TabIndex = 3; + this.radioVOne.TabStop = true; + this.radioVOne.UseVisualStyleBackColor = true; + // + // radioVZero + // + this.radioVZero.AutoSize = true; + this.radioVZero.Location = new System.Drawing.Point(6, 43); + this.radioVZero.Name = "radioVZero"; + this.radioVZero.Size = new System.Drawing.Size(14, 13); + this.radioVZero.TabIndex = 2; + this.radioVZero.TabStop = true; + this.radioVZero.UseVisualStyleBackColor = true; + // + // radioVDefault + // + this.radioVDefault.AutoSize = true; + this.radioVDefault.Location = new System.Drawing.Point(6, 23); + this.radioVDefault.Name = "radioVDefault"; + this.radioVDefault.Size = new System.Drawing.Size(14, 13); + this.radioVDefault.TabIndex = 1; + this.radioVDefault.TabStop = true; + this.radioVDefault.UseVisualStyleBackColor = true; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(281, 205); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 0; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(362, 205); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 1; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // panelM + // + this.panelM.Controls.Add(this.flagMLabel); + this.panelM.Controls.Add(this.radioMIndeterminate); + this.panelM.Controls.Add(this.radioMOne); + this.panelM.Controls.Add(this.radioMZero); + this.panelM.Controls.Add(this.radioMDefault); + this.panelM.Location = new System.Drawing.Point(172, 47); + this.panelM.Name = "panelM"; + this.panelM.Size = new System.Drawing.Size(26, 110); + this.panelM.TabIndex = 15; + // + // flagMLabel + // + this.flagMLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.flagMLabel.Location = new System.Drawing.Point(3, 0); + this.flagMLabel.Name = "flagMLabel"; + this.flagMLabel.Size = new System.Drawing.Size(20, 20); + this.flagMLabel.TabIndex = 0; + this.flagMLabel.Text = "M"; + this.flagMLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // radioMIndeterminate + // + this.radioMIndeterminate.AutoSize = true; + this.radioMIndeterminate.Location = new System.Drawing.Point(6, 83); + this.radioMIndeterminate.Name = "radioMIndeterminate"; + this.radioMIndeterminate.Size = new System.Drawing.Size(14, 13); + this.radioMIndeterminate.TabIndex = 4; + this.radioMIndeterminate.TabStop = true; + this.radioMIndeterminate.UseVisualStyleBackColor = true; + // + // radioMOne + // + this.radioMOne.AutoSize = true; + this.radioMOne.Location = new System.Drawing.Point(6, 63); + this.radioMOne.Name = "radioMOne"; + this.radioMOne.Size = new System.Drawing.Size(14, 13); + this.radioMOne.TabIndex = 3; + this.radioMOne.TabStop = true; + this.radioMOne.UseVisualStyleBackColor = true; + // + // radioMZero + // + this.radioMZero.AutoSize = true; + this.radioMZero.Location = new System.Drawing.Point(6, 43); + this.radioMZero.Name = "radioMZero"; + this.radioMZero.Size = new System.Drawing.Size(14, 13); + this.radioMZero.TabIndex = 2; + this.radioMZero.TabStop = true; + this.radioMZero.UseVisualStyleBackColor = true; + // + // radioMDefault + // + this.radioMDefault.AutoSize = true; + this.radioMDefault.Location = new System.Drawing.Point(6, 23); + this.radioMDefault.Name = "radioMDefault"; + this.radioMDefault.Size = new System.Drawing.Size(14, 13); + this.radioMDefault.TabIndex = 1; + this.radioMDefault.TabStop = true; + this.radioMDefault.UseVisualStyleBackColor = true; + // + // panelX + // + this.panelX.Controls.Add(this.flagXLabel); + this.panelX.Controls.Add(this.radioXIndeterminate); + this.panelX.Controls.Add(this.radioXOne); + this.panelX.Controls.Add(this.radioXZero); + this.panelX.Controls.Add(this.radioXDefault); + this.panelX.Location = new System.Drawing.Point(204, 47); + this.panelX.Name = "panelX"; + this.panelX.Size = new System.Drawing.Size(26, 110); + this.panelX.TabIndex = 16; + // + // flagXLabel + // + this.flagXLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.flagXLabel.Location = new System.Drawing.Point(3, 0); + this.flagXLabel.Name = "flagXLabel"; + this.flagXLabel.Size = new System.Drawing.Size(20, 20); + this.flagXLabel.TabIndex = 0; + this.flagXLabel.Text = "X"; + this.flagXLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // radioXIndeterminate + // + this.radioXIndeterminate.AutoSize = true; + this.radioXIndeterminate.Location = new System.Drawing.Point(6, 83); + this.radioXIndeterminate.Name = "radioXIndeterminate"; + this.radioXIndeterminate.Size = new System.Drawing.Size(14, 13); + this.radioXIndeterminate.TabIndex = 4; + this.radioXIndeterminate.TabStop = true; + this.radioXIndeterminate.UseVisualStyleBackColor = true; + // + // radioXOne + // + this.radioXOne.AutoSize = true; + this.radioXOne.Location = new System.Drawing.Point(6, 63); + this.radioXOne.Name = "radioXOne"; + this.radioXOne.Size = new System.Drawing.Size(14, 13); + this.radioXOne.TabIndex = 3; + this.radioXOne.TabStop = true; + this.radioXOne.UseVisualStyleBackColor = true; + // + // radioXZero + // + this.radioXZero.AutoSize = true; + this.radioXZero.Location = new System.Drawing.Point(6, 43); + this.radioXZero.Name = "radioXZero"; + this.radioXZero.Size = new System.Drawing.Size(14, 13); + this.radioXZero.TabIndex = 2; + this.radioXZero.TabStop = true; + this.radioXZero.UseVisualStyleBackColor = true; + // + // radioXDefault + // + this.radioXDefault.AutoSize = true; + this.radioXDefault.Location = new System.Drawing.Point(6, 23); + this.radioXDefault.Name = "radioXDefault"; + this.radioXDefault.Size = new System.Drawing.Size(14, 13); + this.radioXDefault.TabIndex = 1; + this.radioXDefault.TabStop = true; + this.radioXDefault.UseVisualStyleBackColor = true; + // + // panelD + // + this.panelD.Controls.Add(this.flagDLabel); + this.panelD.Controls.Add(this.radioDIndeterminate); + this.panelD.Controls.Add(this.radioDOne); + this.panelD.Controls.Add(this.radioDZero); + this.panelD.Controls.Add(this.radioDDefault); + this.panelD.Location = new System.Drawing.Point(236, 47); + this.panelD.Name = "panelD"; + this.panelD.Size = new System.Drawing.Size(26, 110); + this.panelD.TabIndex = 17; + // + // flagDLabel + // + this.flagDLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.flagDLabel.Location = new System.Drawing.Point(3, 0); + this.flagDLabel.Name = "flagDLabel"; + this.flagDLabel.Size = new System.Drawing.Size(20, 20); + this.flagDLabel.TabIndex = 0; + this.flagDLabel.Text = "D"; + this.flagDLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // radioDIndeterminate + // + this.radioDIndeterminate.AutoSize = true; + this.radioDIndeterminate.Location = new System.Drawing.Point(6, 83); + this.radioDIndeterminate.Name = "radioDIndeterminate"; + this.radioDIndeterminate.Size = new System.Drawing.Size(14, 13); + this.radioDIndeterminate.TabIndex = 4; + this.radioDIndeterminate.TabStop = true; + this.radioDIndeterminate.UseVisualStyleBackColor = true; + // + // radioDOne + // + this.radioDOne.AutoSize = true; + this.radioDOne.Location = new System.Drawing.Point(6, 63); + this.radioDOne.Name = "radioDOne"; + this.radioDOne.Size = new System.Drawing.Size(14, 13); + this.radioDOne.TabIndex = 3; + this.radioDOne.TabStop = true; + this.radioDOne.UseVisualStyleBackColor = true; + // + // radioDZero + // + this.radioDZero.AutoSize = true; + this.radioDZero.Location = new System.Drawing.Point(6, 43); + this.radioDZero.Name = "radioDZero"; + this.radioDZero.Size = new System.Drawing.Size(14, 13); + this.radioDZero.TabIndex = 2; + this.radioDZero.TabStop = true; + this.radioDZero.UseVisualStyleBackColor = true; + // + // radioDDefault + // + this.radioDDefault.AutoSize = true; + this.radioDDefault.Location = new System.Drawing.Point(6, 23); + this.radioDDefault.Name = "radioDDefault"; + this.radioDDefault.Size = new System.Drawing.Size(14, 13); + this.radioDDefault.TabIndex = 1; + this.radioDDefault.TabStop = true; + this.radioDDefault.UseVisualStyleBackColor = true; + // + // panelI + // + this.panelI.Controls.Add(this.flagILabel); + this.panelI.Controls.Add(this.radioIIndeterminate); + this.panelI.Controls.Add(this.radioIOne); + this.panelI.Controls.Add(this.radioIZero); + this.panelI.Controls.Add(this.radioIDefault); + this.panelI.Location = new System.Drawing.Point(268, 47); + this.panelI.Name = "panelI"; + this.panelI.Size = new System.Drawing.Size(26, 110); + this.panelI.TabIndex = 18; + // + // flagILabel + // + this.flagILabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.flagILabel.Location = new System.Drawing.Point(3, 0); + this.flagILabel.Name = "flagILabel"; + this.flagILabel.Size = new System.Drawing.Size(20, 20); + this.flagILabel.TabIndex = 0; + this.flagILabel.Text = "I"; + this.flagILabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // radioIIndeterminate + // + this.radioIIndeterminate.AutoSize = true; + this.radioIIndeterminate.Location = new System.Drawing.Point(6, 83); + this.radioIIndeterminate.Name = "radioIIndeterminate"; + this.radioIIndeterminate.Size = new System.Drawing.Size(14, 13); + this.radioIIndeterminate.TabIndex = 4; + this.radioIIndeterminate.TabStop = true; + this.radioIIndeterminate.UseVisualStyleBackColor = true; + // + // radioIOne + // + this.radioIOne.AutoSize = true; + this.radioIOne.Location = new System.Drawing.Point(6, 63); + this.radioIOne.Name = "radioIOne"; + this.radioIOne.Size = new System.Drawing.Size(14, 13); + this.radioIOne.TabIndex = 3; + this.radioIOne.TabStop = true; + this.radioIOne.UseVisualStyleBackColor = true; + // + // radioIZero + // + this.radioIZero.AutoSize = true; + this.radioIZero.Location = new System.Drawing.Point(6, 43); + this.radioIZero.Name = "radioIZero"; + this.radioIZero.Size = new System.Drawing.Size(14, 13); + this.radioIZero.TabIndex = 2; + this.radioIZero.TabStop = true; + this.radioIZero.UseVisualStyleBackColor = true; + // + // radioIDefault + // + this.radioIDefault.AutoSize = true; + this.radioIDefault.Location = new System.Drawing.Point(6, 23); + this.radioIDefault.Name = "radioIDefault"; + this.radioIDefault.Size = new System.Drawing.Size(14, 13); + this.radioIDefault.TabIndex = 1; + this.radioIDefault.TabStop = true; + this.radioIDefault.UseVisualStyleBackColor = true; + // + // panelZ + // + this.panelZ.Controls.Add(this.flagZLabel); + this.panelZ.Controls.Add(this.radioZIndeterminate); + this.panelZ.Controls.Add(this.radioZOne); + this.panelZ.Controls.Add(this.radioZZero); + this.panelZ.Controls.Add(this.radioZDefault); + this.panelZ.Location = new System.Drawing.Point(300, 47); + this.panelZ.Name = "panelZ"; + this.panelZ.Size = new System.Drawing.Size(26, 110); + this.panelZ.TabIndex = 19; + // + // flagZLabel + // + this.flagZLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.flagZLabel.Location = new System.Drawing.Point(3, 0); + this.flagZLabel.Name = "flagZLabel"; + this.flagZLabel.Size = new System.Drawing.Size(20, 20); + this.flagZLabel.TabIndex = 0; + this.flagZLabel.Text = "Z"; + this.flagZLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // radioZIndeterminate + // + this.radioZIndeterminate.AutoSize = true; + this.radioZIndeterminate.Location = new System.Drawing.Point(6, 83); + this.radioZIndeterminate.Name = "radioZIndeterminate"; + this.radioZIndeterminate.Size = new System.Drawing.Size(14, 13); + this.radioZIndeterminate.TabIndex = 4; + this.radioZIndeterminate.TabStop = true; + this.radioZIndeterminate.UseVisualStyleBackColor = true; + // + // radioZOne + // + this.radioZOne.AutoSize = true; + this.radioZOne.Location = new System.Drawing.Point(6, 63); + this.radioZOne.Name = "radioZOne"; + this.radioZOne.Size = new System.Drawing.Size(14, 13); + this.radioZOne.TabIndex = 3; + this.radioZOne.TabStop = true; + this.radioZOne.UseVisualStyleBackColor = true; + // + // radioZZero + // + this.radioZZero.AutoSize = true; + this.radioZZero.Location = new System.Drawing.Point(6, 43); + this.radioZZero.Name = "radioZZero"; + this.radioZZero.Size = new System.Drawing.Size(14, 13); + this.radioZZero.TabIndex = 2; + this.radioZZero.TabStop = true; + this.radioZZero.UseVisualStyleBackColor = true; + // + // radioZDefault + // + this.radioZDefault.AutoSize = true; + this.radioZDefault.Location = new System.Drawing.Point(6, 23); + this.radioZDefault.Name = "radioZDefault"; + this.radioZDefault.Size = new System.Drawing.Size(14, 13); + this.radioZDefault.TabIndex = 1; + this.radioZDefault.TabStop = true; + this.radioZDefault.UseVisualStyleBackColor = true; + // + // panelC + // + this.panelC.Controls.Add(this.flagCLabel); + this.panelC.Controls.Add(this.radioCIndeterminate); + this.panelC.Controls.Add(this.radioCOne); + this.panelC.Controls.Add(this.radioCZero); + this.panelC.Controls.Add(this.radioCDefault); + this.panelC.Location = new System.Drawing.Point(332, 47); + this.panelC.Name = "panelC"; + this.panelC.Size = new System.Drawing.Size(26, 110); + this.panelC.TabIndex = 20; + // + // flagCLabel + // + this.flagCLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.flagCLabel.Location = new System.Drawing.Point(3, 0); + this.flagCLabel.Name = "flagCLabel"; + this.flagCLabel.Size = new System.Drawing.Size(20, 20); + this.flagCLabel.TabIndex = 0; + this.flagCLabel.Text = "C"; + this.flagCLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // radioCIndeterminate + // + this.radioCIndeterminate.AutoSize = true; + this.radioCIndeterminate.Location = new System.Drawing.Point(6, 83); + this.radioCIndeterminate.Name = "radioCIndeterminate"; + this.radioCIndeterminate.Size = new System.Drawing.Size(14, 13); + this.radioCIndeterminate.TabIndex = 4; + this.radioCIndeterminate.TabStop = true; + this.radioCIndeterminate.UseVisualStyleBackColor = true; + // + // radioCOne + // + this.radioCOne.AutoSize = true; + this.radioCOne.Location = new System.Drawing.Point(6, 63); + this.radioCOne.Name = "radioCOne"; + this.radioCOne.Size = new System.Drawing.Size(14, 13); + this.radioCOne.TabIndex = 3; + this.radioCOne.TabStop = true; + this.radioCOne.UseVisualStyleBackColor = true; + // + // radioCZero + // + this.radioCZero.AutoSize = true; + this.radioCZero.Location = new System.Drawing.Point(6, 43); + this.radioCZero.Name = "radioCZero"; + this.radioCZero.Size = new System.Drawing.Size(14, 13); + this.radioCZero.TabIndex = 2; + this.radioCZero.TabStop = true; + this.radioCZero.UseVisualStyleBackColor = true; + // + // radioCDefault + // + this.radioCDefault.AutoSize = true; + this.radioCDefault.Location = new System.Drawing.Point(6, 23); + this.radioCDefault.Name = "radioCDefault"; + this.radioCDefault.Size = new System.Drawing.Size(14, 13); + this.radioCDefault.TabIndex = 1; + this.radioCDefault.TabStop = true; + this.radioCDefault.UseVisualStyleBackColor = true; + // + // panelE + // + this.panelE.Controls.Add(this.flagELabel); + this.panelE.Controls.Add(this.radioEIndeterminate); + this.panelE.Controls.Add(this.radioEOne); + this.panelE.Controls.Add(this.radioEZero); + this.panelE.Controls.Add(this.radioEDefault); + this.panelE.Location = new System.Drawing.Point(381, 47); + this.panelE.Name = "panelE"; + this.panelE.Size = new System.Drawing.Size(26, 110); + this.panelE.TabIndex = 21; + // + // flagELabel + // + this.flagELabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.flagELabel.Location = new System.Drawing.Point(3, 0); + this.flagELabel.Name = "flagELabel"; + this.flagELabel.Size = new System.Drawing.Size(20, 20); + this.flagELabel.TabIndex = 0; + this.flagELabel.Text = "E"; + this.flagELabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; + // + // radioEIndeterminate + // + this.radioEIndeterminate.AutoSize = true; + this.radioEIndeterminate.Location = new System.Drawing.Point(6, 83); + this.radioEIndeterminate.Name = "radioEIndeterminate"; + this.radioEIndeterminate.Size = new System.Drawing.Size(14, 13); + this.radioEIndeterminate.TabIndex = 4; + this.radioEIndeterminate.TabStop = true; + this.radioEIndeterminate.UseVisualStyleBackColor = true; + // + // radioEOne + // + this.radioEOne.AutoSize = true; + this.radioEOne.Location = new System.Drawing.Point(6, 63); + this.radioEOne.Name = "radioEOne"; + this.radioEOne.Size = new System.Drawing.Size(14, 13); + this.radioEOne.TabIndex = 3; + this.radioEOne.TabStop = true; + this.radioEOne.UseVisualStyleBackColor = true; + // + // radioEZero + // + this.radioEZero.AutoSize = true; + this.radioEZero.Location = new System.Drawing.Point(6, 43); + this.radioEZero.Name = "radioEZero"; + this.radioEZero.Size = new System.Drawing.Size(14, 13); + this.radioEZero.TabIndex = 2; + this.radioEZero.TabStop = true; + this.radioEZero.UseVisualStyleBackColor = true; + // + // radioEDefault + // + this.radioEDefault.AutoSize = true; + this.radioEDefault.Location = new System.Drawing.Point(6, 23); + this.radioEDefault.Name = "radioEDefault"; + this.radioEDefault.Size = new System.Drawing.Size(14, 13); + this.radioEDefault.TabIndex = 1; + this.radioEDefault.TabStop = true; + this.radioEDefault.UseVisualStyleBackColor = true; + // + // instructionLabel + // + this.instructionLabel.AutoSize = true; + this.instructionLabel.Location = new System.Drawing.Point(12, 9); + this.instructionLabel.Name = "instructionLabel"; + this.instructionLabel.Size = new System.Drawing.Size(297, 13); + this.instructionLabel.TabIndex = 3; + this.instructionLabel.Text = "Override processor state values determined by code analyzer:"; + // + // labelLongRegsNote + // + this.labelLongRegsNote.AutoSize = true; + this.labelLongRegsNote.Location = new System.Drawing.Point(13, 169); + this.labelLongRegsNote.Name = "labelLongRegsNote"; + this.labelLongRegsNote.Size = new System.Drawing.Size(341, 13); + this.labelLongRegsNote.TabIndex = 8; + this.labelLongRegsNote.Text = "Tip: to configure 16-bit registers on 65802/65816, set M, X, and E to 0."; + // + // resetButton + // + this.resetButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.resetButton.Location = new System.Drawing.Point(12, 205); + this.resetButton.Name = "resetButton"; + this.resetButton.Size = new System.Drawing.Size(112, 23); + this.resetButton.TabIndex = 2; + this.resetButton.Text = "Reset to Default"; + this.resetButton.UseVisualStyleBackColor = true; + this.resetButton.Click += new System.EventHandler(this.resetButton_Click); + // + // EditStatusFlags + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(449, 240); + this.Controls.Add(this.resetButton); + this.Controls.Add(this.labelLongRegsNote); + this.Controls.Add(this.instructionLabel); + this.Controls.Add(this.panelE); + this.Controls.Add(this.panelC); + this.Controls.Add(this.panelZ); + this.Controls.Add(this.panelI); + this.Controls.Add(this.panelD); + this.Controls.Add(this.panelX); + this.Controls.Add(this.panelM); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.okButton); + this.Controls.Add(this.panelV); + this.Controls.Add(this.panelN); + this.Controls.Add(this.indeterminateLabel); + this.Controls.Add(this.oneLabel); + this.Controls.Add(this.zeroLabel); + this.Controls.Add(this.defaultLabel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditStatusFlags"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Status Flag Override"; + this.Load += new System.EventHandler(this.EditStatusFlags_Load); + this.panelN.ResumeLayout(false); + this.panelN.PerformLayout(); + this.panelV.ResumeLayout(false); + this.panelV.PerformLayout(); + this.panelM.ResumeLayout(false); + this.panelM.PerformLayout(); + this.panelX.ResumeLayout(false); + this.panelX.PerformLayout(); + this.panelD.ResumeLayout(false); + this.panelD.PerformLayout(); + this.panelI.ResumeLayout(false); + this.panelI.PerformLayout(); + this.panelZ.ResumeLayout(false); + this.panelZ.PerformLayout(); + this.panelC.ResumeLayout(false); + this.panelC.PerformLayout(); + this.panelE.ResumeLayout(false); + this.panelE.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label defaultLabel; + private System.Windows.Forms.Label zeroLabel; + private System.Windows.Forms.Label oneLabel; + private System.Windows.Forms.Label indeterminateLabel; + private System.Windows.Forms.Panel panelN; + private System.Windows.Forms.Label flagNLabel; + private System.Windows.Forms.RadioButton radioNIndeterminate; + private System.Windows.Forms.RadioButton radioNOne; + private System.Windows.Forms.RadioButton radioNZero; + private System.Windows.Forms.RadioButton radioNDefault; + private System.Windows.Forms.Panel panelV; + private System.Windows.Forms.Label flagVLabel; + private System.Windows.Forms.RadioButton radioVIndeterminate; + private System.Windows.Forms.RadioButton radioVOne; + private System.Windows.Forms.RadioButton radioVZero; + private System.Windows.Forms.RadioButton radioVDefault; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Panel panelM; + private System.Windows.Forms.Label flagMLabel; + private System.Windows.Forms.RadioButton radioMIndeterminate; + private System.Windows.Forms.RadioButton radioMOne; + private System.Windows.Forms.RadioButton radioMZero; + private System.Windows.Forms.RadioButton radioMDefault; + private System.Windows.Forms.Panel panelX; + private System.Windows.Forms.Label flagXLabel; + private System.Windows.Forms.RadioButton radioXIndeterminate; + private System.Windows.Forms.RadioButton radioXOne; + private System.Windows.Forms.RadioButton radioXZero; + private System.Windows.Forms.RadioButton radioXDefault; + private System.Windows.Forms.Panel panelD; + private System.Windows.Forms.Label flagDLabel; + private System.Windows.Forms.RadioButton radioDIndeterminate; + private System.Windows.Forms.RadioButton radioDOne; + private System.Windows.Forms.RadioButton radioDZero; + private System.Windows.Forms.RadioButton radioDDefault; + private System.Windows.Forms.Panel panelI; + private System.Windows.Forms.Label flagILabel; + private System.Windows.Forms.RadioButton radioIIndeterminate; + private System.Windows.Forms.RadioButton radioIOne; + private System.Windows.Forms.RadioButton radioIZero; + private System.Windows.Forms.RadioButton radioIDefault; + private System.Windows.Forms.Panel panelZ; + private System.Windows.Forms.Label flagZLabel; + private System.Windows.Forms.RadioButton radioZIndeterminate; + private System.Windows.Forms.RadioButton radioZOne; + private System.Windows.Forms.RadioButton radioZZero; + private System.Windows.Forms.RadioButton radioZDefault; + private System.Windows.Forms.Panel panelC; + private System.Windows.Forms.Label flagCLabel; + private System.Windows.Forms.RadioButton radioCIndeterminate; + private System.Windows.Forms.RadioButton radioCOne; + private System.Windows.Forms.RadioButton radioCZero; + private System.Windows.Forms.RadioButton radioCDefault; + private System.Windows.Forms.Panel panelE; + private System.Windows.Forms.Label flagELabel; + private System.Windows.Forms.RadioButton radioEIndeterminate; + private System.Windows.Forms.RadioButton radioEOne; + private System.Windows.Forms.RadioButton radioEZero; + private System.Windows.Forms.RadioButton radioEDefault; + private System.Windows.Forms.Label instructionLabel; + private System.Windows.Forms.Label labelLongRegsNote; + private System.Windows.Forms.Button resetButton; + } +} diff --git a/SourceGen/AppForms/EditStatusFlags.cs b/SourceGen/AppForms/EditStatusFlags.cs new file mode 100644 index 0000000..448d26d --- /dev/null +++ b/SourceGen/AppForms/EditStatusFlags.cs @@ -0,0 +1,140 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Windows.Forms; + +using Asm65; + +namespace SourceGen.AppForms { + public partial class EditStatusFlags : Form { + /// + /// In/out status flag value. + /// + public StatusFlags FlagValue { get; set; } + + /// + /// Set this if the CPU has an emulation flag (65802/65816). If this isn't + /// set, the M, X, and E flag buttons will be disabled. + /// + public bool HasEmuFlag { get; set; } + + + public EditStatusFlags() { + InitializeComponent(); + } + + private void EditStatusFlags_Load(object sender, EventArgs e) { + if (!HasEmuFlag) { + panelM.Enabled = false; + panelX.Enabled = false; + panelE.Enabled = false; + + // I'm not going to force the M/X/E flags to have a particular value based + // on the CPU definition. The flags aren't used for non-65802/65816, so + // the values are irrelevant. If somebody is switching between CPUs I think + // it'd be weird to force the values during editing but leave any non-edited + // values alone. If they want to switch to 65816, set M/X/E, and then switch + // back, they're welcome to do so. + } + + SetCheckedButtons(); + } + + /// + /// Calls SetChecked() for each flag. + /// + private void SetCheckedButtons() { + SetChecked(FlagValue.N, radioNDefault, radioNZero, radioNOne, radioNIndeterminate); + SetChecked(FlagValue.V, radioVDefault, radioVZero, radioVOne, radioVIndeterminate); + SetChecked(FlagValue.M, radioMDefault, radioMZero, radioMOne, radioMIndeterminate); + SetChecked(FlagValue.X, radioXDefault, radioXZero, radioXOne, radioXIndeterminate); + SetChecked(FlagValue.D, radioDDefault, radioDZero, radioDOne, radioDIndeterminate); + SetChecked(FlagValue.I, radioIDefault, radioIZero, radioIOne, radioIIndeterminate); + SetChecked(FlagValue.Z, radioZDefault, radioZZero, radioZOne, radioZIndeterminate); + SetChecked(FlagValue.C, radioCDefault, radioCZero, radioCOne, radioCIndeterminate); + SetChecked(FlagValue.E, radioEDefault, radioEZero, radioEOne, radioEIndeterminate); + } + + /// + /// Sets the "checked" flag on the appropriate radio button. + /// + private void SetChecked(int value, RadioButton def, RadioButton zero, RadioButton one, + RadioButton indeterminate) { + switch (value) { + case TriState16.UNSPECIFIED: + def.Checked = true; + break; + case TriState16.INDETERMINATE: + indeterminate.Checked = true; + break; + case 0: + zero.Checked = true; + break; + case 1: + one.Checked = true; + break; + default: + throw new Exception("Unexpected value " + value); + } + } + + private void okButton_Click(object sender, EventArgs e) { + StatusFlags flags = new StatusFlags(); + + flags.N = GetChecked(radioNDefault, radioNZero, radioNOne, radioNIndeterminate); + flags.V = GetChecked(radioVDefault, radioVZero, radioVOne, radioVIndeterminate); + flags.M = GetChecked(radioMDefault, radioMZero, radioMOne, radioMIndeterminate); + flags.X = GetChecked(radioXDefault, radioXZero, radioXOne, radioXIndeterminate); + flags.D = GetChecked(radioDDefault, radioDZero, radioDOne, radioDIndeterminate); + flags.I = GetChecked(radioIDefault, radioIZero, radioIOne, radioIIndeterminate); + flags.Z = GetChecked(radioZDefault, radioZZero, radioZOne, radioZIndeterminate); + flags.C = GetChecked(radioCDefault, radioCZero, radioCOne, radioCIndeterminate); + flags.E = GetChecked(radioEDefault, radioEZero, radioEOne, radioEIndeterminate); + + //// If they're setting emulation mode, also set M/X to 1. This is implicitly + //// true, but things are a bit clearer if we make it explicit. + //if (flags.E == 1) { + // flags.M = flags.X = 1; + //} + + FlagValue = flags; + } + + /// + /// Identifies the checked radio button and returns the appropriate TriState16 value. + /// + private int GetChecked(RadioButton def, RadioButton zero, RadioButton one, + RadioButton indeterminate) { + if (zero.Checked) { + return 0; + } else if (one.Checked) { + return 1; + } else if (indeterminate.Checked) { + return TriState16.INDETERMINATE; + } else if (def.Checked) { + return TriState16.UNSPECIFIED; + } else { + throw new Exception("No radio button selected"); + } + } + + private void resetButton_Click(object sender, EventArgs e) { + FlagValue = new StatusFlags(); + SetCheckedButtons(); + } + } +} diff --git a/SourceGen/AppForms/EditStatusFlags.resx b/SourceGen/AppForms/EditStatusFlags.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/EditStatusFlags.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/FindBox.Designer.cs b/SourceGen/AppForms/FindBox.Designer.cs new file mode 100644 index 0000000..dc39102 --- /dev/null +++ b/SourceGen/AppForms/FindBox.Designer.cs @@ -0,0 +1,89 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class FindBox { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.okButton = new System.Windows.Forms.Button(); + this.findTextBox = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(222, 13); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 1; + this.okButton.Text = "Find"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // findTextBox + // + this.findTextBox.Location = new System.Drawing.Point(12, 15); + this.findTextBox.Name = "findTextBox"; + this.findTextBox.Size = new System.Drawing.Size(197, 20); + this.findTextBox.TabIndex = 0; + // + // FindBox + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(309, 48); + this.Controls.Add(this.findTextBox); + this.Controls.Add(this.okButton); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "FindBox"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Find..."; + this.Load += new System.EventHandler(this.FindBox_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.TextBox findTextBox; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/FindBox.cs b/SourceGen/AppForms/FindBox.cs new file mode 100644 index 0000000..333ecf2 --- /dev/null +++ b/SourceGen/AppForms/FindBox.cs @@ -0,0 +1,50 @@ +/* + * Copyright 2018 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.Windows.Forms; + +namespace SourceGen.AppForms { + public partial class FindBox : Form { + /// + /// Text to find. + /// + public string TextToFind { get; set; } + + public FindBox() { + InitializeComponent(); + } + + private void FindBox_Load(object sender, EventArgs e) { + if (!string.IsNullOrEmpty(TextToFind)) { + findTextBox.Text = TextToFind; + findTextBox.SelectAll(); + } + } + + // Without a "cancel" button, the escape key does nothing. + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { + if (keyData == Keys.Escape) { + Close(); + return true; + } + return base.ProcessCmdKey(ref msg, keyData); + } + + private void okButton_Click(object sender, EventArgs e) { + TextToFind = findTextBox.Text; + } + } +} diff --git a/SourceGen/AppForms/FindBox.resx b/SourceGen/AppForms/FindBox.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/FindBox.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/GotoBox.Designer.cs b/SourceGen/AppForms/GotoBox.Designer.cs new file mode 100644 index 0000000..6891231 --- /dev/null +++ b/SourceGen/AppForms/GotoBox.Designer.cs @@ -0,0 +1,181 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class GotoBox { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.instructionLabel = new System.Windows.Forms.Label(); + this.targetTextBox = new System.Windows.Forms.TextBox(); + this.okButton = new System.Windows.Forms.Button(); + this.offsetLabel = new System.Windows.Forms.Label(); + this.addressLabel = new System.Windows.Forms.Label(); + this.labelLabel = new System.Windows.Forms.Label(); + this.addressValueLabel = new System.Windows.Forms.Label(); + this.offsetValueLabel = new System.Windows.Forms.Label(); + this.labelValueLabel = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // instructionLabel + // + this.instructionLabel.AutoSize = true; + this.instructionLabel.Location = new System.Drawing.Point(13, 13); + this.instructionLabel.Name = "instructionLabel"; + this.instructionLabel.Size = new System.Drawing.Size(215, 52); + this.instructionLabel.TabIndex = 0; + this.instructionLabel.Text = "Enter target location as one of:\r\n • Hex file offset (with \'+\', e.g. +500)\r\n • He" + + "x address (e.g. 1000, $1000, 00/1000)\r\n • Label (case-sensitive)\r\n"; + // + // targetTextBox + // + this.targetTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.targetTextBox.Location = new System.Drawing.Point(13, 78); + this.targetTextBox.Name = "targetTextBox"; + this.targetTextBox.Size = new System.Drawing.Size(215, 20); + this.targetTextBox.TabIndex = 1; + this.targetTextBox.TextChanged += new System.EventHandler(this.targetTextBox_TextChanged); + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(240, 76); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 2; + this.okButton.Text = "Go"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // offsetLabel + // + this.offsetLabel.AutoSize = true; + this.offsetLabel.Location = new System.Drawing.Point(13, 111); + this.offsetLabel.Name = "offsetLabel"; + this.offsetLabel.Size = new System.Drawing.Size(38, 13); + this.offsetLabel.TabIndex = 3; + this.offsetLabel.Text = "Offset:"; + // + // addressLabel + // + this.addressLabel.AutoSize = true; + this.addressLabel.Location = new System.Drawing.Point(12, 129); + this.addressLabel.Name = "addressLabel"; + this.addressLabel.Size = new System.Drawing.Size(48, 13); + this.addressLabel.TabIndex = 4; + this.addressLabel.Text = "Address:"; + // + // labelLabel + // + this.labelLabel.AutoSize = true; + this.labelLabel.Location = new System.Drawing.Point(12, 147); + this.labelLabel.Name = "labelLabel"; + this.labelLabel.Size = new System.Drawing.Size(36, 13); + this.labelLabel.TabIndex = 5; + this.labelLabel.Text = "Label:"; + // + // addressValueLabel + // + this.addressValueLabel.AutoSize = true; + this.addressValueLabel.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.addressValueLabel.Location = new System.Drawing.Point(66, 129); + this.addressValueLabel.Name = "addressValueLabel"; + this.addressValueLabel.Size = new System.Drawing.Size(49, 13); + this.addressValueLabel.TabIndex = 6; + this.addressValueLabel.Text = "01/2345"; + // + // offsetValueLabel + // + this.offsetValueLabel.AutoSize = true; + this.offsetValueLabel.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.offsetValueLabel.Location = new System.Drawing.Point(66, 111); + this.offsetValueLabel.Name = "offsetValueLabel"; + this.offsetValueLabel.Size = new System.Drawing.Size(37, 13); + this.offsetValueLabel.TabIndex = 7; + this.offsetValueLabel.Text = "+1234"; + // + // labelValueLabel + // + this.labelValueLabel.AutoSize = true; + this.labelValueLabel.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.labelValueLabel.Location = new System.Drawing.Point(66, 147); + this.labelValueLabel.Name = "labelValueLabel"; + this.labelValueLabel.Size = new System.Drawing.Size(37, 13); + this.labelValueLabel.TabIndex = 8; + this.labelValueLabel.Text = "FUBAR"; + // + // GotoBox + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(327, 171); + this.Controls.Add(this.labelValueLabel); + this.Controls.Add(this.offsetValueLabel); + this.Controls.Add(this.addressValueLabel); + this.Controls.Add(this.labelLabel); + this.Controls.Add(this.addressLabel); + this.Controls.Add(this.offsetLabel); + this.Controls.Add(this.okButton); + this.Controls.Add(this.targetTextBox); + this.Controls.Add(this.instructionLabel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "GotoBox"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Go To Line"; + this.Load += new System.EventHandler(this.GotoBox_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label instructionLabel; + private System.Windows.Forms.TextBox targetTextBox; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Label offsetLabel; + private System.Windows.Forms.Label addressLabel; + private System.Windows.Forms.Label labelLabel; + private System.Windows.Forms.Label addressValueLabel; + private System.Windows.Forms.Label offsetValueLabel; + private System.Windows.Forms.Label labelValueLabel; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/GotoBox.cs b/SourceGen/AppForms/GotoBox.cs new file mode 100644 index 0000000..f4c5245 --- /dev/null +++ b/SourceGen/AppForms/GotoBox.cs @@ -0,0 +1,119 @@ +/* + * Copyright 2018 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.Windows.Forms; + +using Asm65; + +namespace SourceGen.AppForms { + public partial class GotoBox : Form { + /// + /// On success, this will hold the target offset. + /// + public int TargetOffset { get; private set; } + + /// + /// Reference to project. + /// + private DisasmProject mProject; + + /// + /// Reference to formatter. This determines how values are displayed. + /// + private Formatter mFormatter; + + + public GotoBox(DisasmProject proj, Formatter formatter) { + InitializeComponent(); + + mProject = proj; + mFormatter = formatter; + TargetOffset = -1; + } + + private void GotoBox_Load(object sender, EventArgs e) { + UpdateDisplay(); + } + + // Without a "cancel" button, the escape key does nothing. + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { + if (keyData == Keys.Escape) { + Close(); + return true; + } + return base.ProcessCmdKey(ref msg, keyData); + } + + private void okButton_Click(object sender, EventArgs e) { + } + + private void targetTextBox_TextChanged(object sender, EventArgs e) { + ProcessInput(); + UpdateDisplay(); + okButton.Enabled = (TargetOffset >= 0); + } + + private void ProcessInput() { + TargetOffset = -1; + + string input = targetTextBox.Text.Trim(); + if (string.IsNullOrEmpty(input)) { + return; + } + if (input[0] == '+') { + // this can only be an offset; convert as hexadecimal number + try { + TargetOffset = Convert.ToInt32(input.Substring(1), 16); + } catch (Exception) { + } + return; + } + + // Try it as a label. If they give the label a hex name (e.g. "A001") they + // can prefix it with '$' to disambiguate the address. + int labelOffset = mProject.FindLabelByName(input); + if (labelOffset >= 0) { + TargetOffset = labelOffset; + } else if (Address.ParseAddress(input, 1<<24, out int addr)) { + // could be a valid address + int offset = mProject.AddrMap.AddressToOffset(0, addr); + if (offset >= 0) { + TargetOffset = offset; + } + } + } + + private void UpdateDisplay() { + string offsetStr = string.Empty; + string addressStr = string.Empty; + string labelStr = string.Empty; + + if (TargetOffset >= 0) { + offsetStr = mFormatter.FormatOffset24(TargetOffset); + int addr = mProject.GetAnattrib(TargetOffset).Address; + addressStr = mFormatter.FormatAddress(addr, addr > 0xffff); + Symbol sym = mProject.GetAnattrib(TargetOffset).Symbol; + if (sym != null) { + labelStr = sym.Label; + } + } + + offsetValueLabel.Text = offsetStr; + addressValueLabel.Text = addressStr; + labelValueLabel.Text = labelStr; + } + } +} diff --git a/SourceGen/AppForms/GotoBox.resx b/SourceGen/AppForms/GotoBox.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/GotoBox.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/ProjectLoadIssues.Designer.cs b/SourceGen/AppForms/ProjectLoadIssues.Designer.cs new file mode 100644 index 0000000..2a67e4a --- /dev/null +++ b/SourceGen/AppForms/ProjectLoadIssues.Designer.cs @@ -0,0 +1,134 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms { + partial class ProjectLoadIssues { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.cancelButton = new System.Windows.Forms.Button(); + this.okButton = new System.Windows.Forms.Button(); + this.labelSomeIssues = new System.Windows.Forms.Label(); + this.messageTextBox = new System.Windows.Forms.TextBox(); + this.invalidDiscardLabel = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(469, 184); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 1; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(388, 184); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 0; + this.okButton.Text = "Continue"; + this.okButton.UseVisualStyleBackColor = true; + // + // labelSomeIssues + // + this.labelSomeIssues.AutoSize = true; + this.labelSomeIssues.Location = new System.Drawing.Point(13, 13); + this.labelSomeIssues.Name = "labelSomeIssues"; + this.labelSomeIssues.Size = new System.Drawing.Size(257, 13); + this.labelSomeIssues.TabIndex = 2; + this.labelSomeIssues.Text = "Problems were detected while loading the project file:"; + // + // messageTextBox + // + this.messageTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.messageTextBox.BackColor = System.Drawing.SystemColors.Window; + this.messageTextBox.Location = new System.Drawing.Point(12, 37); + this.messageTextBox.Multiline = true; + this.messageTextBox.Name = "messageTextBox"; + this.messageTextBox.ReadOnly = true; + this.messageTextBox.Size = new System.Drawing.Size(533, 113); + this.messageTextBox.TabIndex = 3; + // + // invalidDiscardLabel + // + this.invalidDiscardLabel.AutoSize = true; + this.invalidDiscardLabel.ForeColor = System.Drawing.Color.Red; + this.invalidDiscardLabel.Location = new System.Drawing.Point(13, 158); + this.invalidDiscardLabel.Name = "invalidDiscardLabel"; + this.invalidDiscardLabel.Size = new System.Drawing.Size(301, 13); + this.invalidDiscardLabel.TabIndex = 4; + this.invalidDiscardLabel.Text = "Invalid data items will be discarded when you save the project."; + // + // ProjectLoadIssues + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(556, 219); + this.Controls.Add(this.invalidDiscardLabel); + this.Controls.Add(this.messageTextBox); + this.Controls.Add(this.labelSomeIssues); + this.Controls.Add(this.okButton); + this.Controls.Add(this.cancelButton); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ProjectLoadIssues"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Project Load Issues"; + this.Load += new System.EventHandler(this.ProjectLoadIssues_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Label labelSomeIssues; + private System.Windows.Forms.TextBox messageTextBox; + private System.Windows.Forms.Label invalidDiscardLabel; + } +} \ No newline at end of file diff --git a/SourceGen/AppForms/ProjectLoadIssues.cs b/SourceGen/AppForms/ProjectLoadIssues.cs new file mode 100644 index 0000000..084b81b --- /dev/null +++ b/SourceGen/AppForms/ProjectLoadIssues.cs @@ -0,0 +1,63 @@ +/* + * Copyright 2018 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.Drawing; +using System.Windows.Forms; + +namespace SourceGen.AppForms { + /// + /// Display errors and warnings generated while attempting to open a project. + /// + public partial class ProjectLoadIssues : Form { + /// + /// Multi-line message for text box. + /// + public string Messages { get; set; } + + /// + /// Enable or disable the Continue button. Defaults to true. + /// + public bool CanContinue { get; set; } + + /// + /// Enable or disable the Cancel button. Defaults to true. + /// + public bool CanCancel { get; set; } + + + public ProjectLoadIssues() { + InitializeComponent(); + CanContinue = CanCancel = true; + } + + private void ProjectLoadIssues_Load(object sender, EventArgs e) { + messageTextBox.Text = Messages; + + if (!CanContinue) { + okButton.Enabled = false; + + // No point warning them about invalid data if they can't continue. + invalidDiscardLabel.Visible = false; + } + if (!CanCancel) { + cancelButton.Enabled = false; + + // They're stuck with the problem. + invalidDiscardLabel.Visible = false; + } + } + } +} diff --git a/SourceGen/AppForms/ProjectLoadIssues.resx b/SourceGen/AppForms/ProjectLoadIssues.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AppForms/ProjectLoadIssues.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AppForms/ProjectView.Designer.cs b/SourceGen/AppForms/ProjectView.Designer.cs new file mode 100644 index 0000000..72a2a68 --- /dev/null +++ b/SourceGen/AppForms/ProjectView.Designer.cs @@ -0,0 +1,1616 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AppForms +{ + partial class ProjectView + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ProjectView)); + this.mainMenuStrip = new System.Windows.Forms.MenuStrip(); + this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.newToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.closeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator(); + this.assembleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.printToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator(); + this.recentProjectsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.noRecentsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem10 = new System.Windows.Forms.ToolStripSeparator(); + this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.undoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.redoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); + this.selectAllToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.findToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.findNextToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.gotoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); + this.editHeaderCommentToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.projectPropertiesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator(); + this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.actionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.setAddressToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.overrideStatusFlagsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.editLabelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.editOperandToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.editDataFormatToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.editCommentToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.editLongCommentToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.editNoteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.editProjectSymbolToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.actionsMenuSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.hintAsCodeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.hintAsDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.hintAsInlineDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.removeHintToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem11 = new System.Windows.Forms.ToolStripSeparator(); + this.toggleSingleBytesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.deleteNoteCommentToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem9 = new System.Windows.Forms.ToolStripSeparator(); + this.showHexDumpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.hexDumpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.aSCIIChartToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.contentsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.dEBUGToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.reanalyzeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.showUndoRedoHistoryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.showAnalyzerOutputToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.showAnalysisTimersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.extensionScriptInfoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator(); + this.toggleOwnerDrawToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toggleCommentRulersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.useKeepAliveHackToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripSeparator(); + this.sourceGenTestsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mainStatusStrip = new System.Windows.Forms.StatusStrip(); + this.toolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.mainToolStrip = new System.Windows.Forms.ToolStrip(); + this.navigateBackToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.navigateFwdToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.newToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.openToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.saveToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.printToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator = new System.Windows.Forms.ToolStripSeparator(); + this.cutToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.copyToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.pasteToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.helpToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.mainSplitterLeft = new System.Windows.Forms.SplitContainer(); + this.leftPanelSplitter = new System.Windows.Forms.SplitContainer(); + this.referencesGroupBox = new System.Windows.Forms.GroupBox(); + this.referencesListView = new System.Windows.Forms.ListView(); + this.offsetColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.addressColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.typeColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.notesGroupBox = new System.Windows.Forms.GroupBox(); + this.notesListView = new System.Windows.Forms.ListView(); + this.notesOffsetColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.notesNoteColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.mainSplitterRight = new System.Windows.Forms.SplitContainer(); + this.noProjectPanel = new System.Windows.Forms.Panel(); + this.debugModeEnabledLabel = new System.Windows.Forms.Label(); + this.recentProjectsLabel = new System.Windows.Forms.Label(); + this.recentProjectLabel2 = new System.Windows.Forms.LinkLabel(); + this.versionLabel = new System.Windows.Forms.Label(); + this.sourceGenLabel = new System.Windows.Forms.Label(); + this.logoPictureBox = new System.Windows.Forms.PictureBox(); + this.recentProjectLabel1 = new System.Windows.Forms.LinkLabel(); + this.openExistingLabel = new System.Windows.Forms.LinkLabel(); + this.newProjectLink = new System.Windows.Forms.LinkLabel(); + this.codeListView = new System.Windows.Forms.ListView(); + this.columnOffset = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnAddr = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnBytes = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnFlags = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnAttr = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnLabel = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnOpcode = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnOperand = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnComment = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.rightPanelSplitter = new System.Windows.Forms.SplitContainer(); + this.symbolsGroupBox = new System.Windows.Forms.GroupBox(); + this.symbolUserCheckBox = new System.Windows.Forms.CheckBox(); + this.symbolProjectCheckBox = new System.Windows.Forms.CheckBox(); + this.symbolAddressCheckBox = new System.Windows.Forms.CheckBox(); + this.symbolConstantCheckBox = new System.Windows.Forms.CheckBox(); + this.symbolAutoCheckBox = new System.Windows.Forms.CheckBox(); + this.symbolPlatformCheckBox = new System.Windows.Forms.CheckBox(); + this.symbolListView = new System.Windows.Forms.ListView(); + this.symbolTypeColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.symbolNameColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.symbolValueColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.infoGroupBox = new System.Windows.Forms.GroupBox(); + this.infoTextBox = new System.Windows.Forms.TextBox(); + this.mainMenuStrip.SuspendLayout(); + this.mainStatusStrip.SuspendLayout(); + this.mainToolStrip.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.mainSplitterLeft)).BeginInit(); + this.mainSplitterLeft.Panel1.SuspendLayout(); + this.mainSplitterLeft.Panel2.SuspendLayout(); + this.mainSplitterLeft.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.leftPanelSplitter)).BeginInit(); + this.leftPanelSplitter.Panel1.SuspendLayout(); + this.leftPanelSplitter.Panel2.SuspendLayout(); + this.leftPanelSplitter.SuspendLayout(); + this.referencesGroupBox.SuspendLayout(); + this.notesGroupBox.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.mainSplitterRight)).BeginInit(); + this.mainSplitterRight.Panel1.SuspendLayout(); + this.mainSplitterRight.Panel2.SuspendLayout(); + this.mainSplitterRight.SuspendLayout(); + this.noProjectPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.rightPanelSplitter)).BeginInit(); + this.rightPanelSplitter.Panel1.SuspendLayout(); + this.rightPanelSplitter.Panel2.SuspendLayout(); + this.rightPanelSplitter.SuspendLayout(); + this.symbolsGroupBox.SuspendLayout(); + this.infoGroupBox.SuspendLayout(); + this.SuspendLayout(); + // + // mainMenuStrip + // + this.mainMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.fileToolStripMenuItem, + this.editToolStripMenuItem, + this.actionsToolStripMenuItem, + this.toolsToolStripMenuItem, + this.helpToolStripMenuItem, + this.dEBUGToolStripMenuItem}); + this.mainMenuStrip.Location = new System.Drawing.Point(0, 0); + this.mainMenuStrip.Name = "mainMenuStrip"; + this.mainMenuStrip.Size = new System.Drawing.Size(805, 24); + this.mainMenuStrip.TabIndex = 0; + this.mainMenuStrip.Text = "Main MenuStrip"; + // + // fileToolStripMenuItem + // + this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.newToolStripMenuItem, + this.openToolStripMenuItem, + this.saveToolStripMenuItem, + this.saveAsToolStripMenuItem, + this.closeToolStripMenuItem, + this.toolStripMenuItem4, + this.assembleToolStripMenuItem, + this.printToolStripMenuItem, + this.toolStripMenuItem5, + this.recentProjectsToolStripMenuItem, + this.toolStripMenuItem10, + this.exitToolStripMenuItem}); + this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; + this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); + this.fileToolStripMenuItem.Text = "File"; + // + // newToolStripMenuItem + // + this.newToolStripMenuItem.Name = "newToolStripMenuItem"; + this.newToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.N))); + this.newToolStripMenuItem.Size = new System.Drawing.Size(208, 22); + this.newToolStripMenuItem.Text = "New"; + this.newToolStripMenuItem.Click += new System.EventHandler(this.newToolStripMenuItem_Click); + // + // openToolStripMenuItem + // + this.openToolStripMenuItem.Name = "openToolStripMenuItem"; + this.openToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); + this.openToolStripMenuItem.Size = new System.Drawing.Size(208, 22); + this.openToolStripMenuItem.Text = "Open"; + this.openToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click); + // + // saveToolStripMenuItem + // + this.saveToolStripMenuItem.Name = "saveToolStripMenuItem"; + this.saveToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S))); + this.saveToolStripMenuItem.Size = new System.Drawing.Size(208, 22); + this.saveToolStripMenuItem.Text = "Save"; + this.saveToolStripMenuItem.Click += new System.EventHandler(this.saveToolStripMenuItem_Click); + // + // saveAsToolStripMenuItem + // + this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem"; + this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(208, 22); + this.saveAsToolStripMenuItem.Text = "Save As..."; + this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.saveAsToolStripMenuItem_Click); + // + // closeToolStripMenuItem + // + this.closeToolStripMenuItem.Name = "closeToolStripMenuItem"; + this.closeToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.W))); + this.closeToolStripMenuItem.Size = new System.Drawing.Size(208, 22); + this.closeToolStripMenuItem.Text = "Close"; + this.closeToolStripMenuItem.Click += new System.EventHandler(this.closeToolStripMenuItem_Click); + // + // toolStripMenuItem4 + // + this.toolStripMenuItem4.Name = "toolStripMenuItem4"; + this.toolStripMenuItem4.Size = new System.Drawing.Size(205, 6); + // + // assembleToolStripMenuItem + // + this.assembleToolStripMenuItem.Name = "assembleToolStripMenuItem"; + this.assembleToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) + | System.Windows.Forms.Keys.A))); + this.assembleToolStripMenuItem.Size = new System.Drawing.Size(208, 22); + this.assembleToolStripMenuItem.Text = "Assemble..."; + this.assembleToolStripMenuItem.Click += new System.EventHandler(this.assembleToolStripMenuItem_Click); + // + // printToolStripMenuItem + // + this.printToolStripMenuItem.Name = "printToolStripMenuItem"; + this.printToolStripMenuItem.Size = new System.Drawing.Size(208, 22); + this.printToolStripMenuItem.Text = "Print..."; + this.printToolStripMenuItem.Visible = false; + // + // toolStripMenuItem5 + // + this.toolStripMenuItem5.Name = "toolStripMenuItem5"; + this.toolStripMenuItem5.Size = new System.Drawing.Size(205, 6); + // + // recentProjectsToolStripMenuItem + // + this.recentProjectsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.noRecentsToolStripMenuItem}); + this.recentProjectsToolStripMenuItem.Name = "recentProjectsToolStripMenuItem"; + this.recentProjectsToolStripMenuItem.Size = new System.Drawing.Size(208, 22); + this.recentProjectsToolStripMenuItem.Text = "Recent Projects"; + this.recentProjectsToolStripMenuItem.DropDownOpening += new System.EventHandler(this.recentProjectsToolStripMenuItem_DropDownOpening); + // + // noRecentsToolStripMenuItem + // + this.noRecentsToolStripMenuItem.Enabled = false; + this.noRecentsToolStripMenuItem.Name = "noRecentsToolStripMenuItem"; + this.noRecentsToolStripMenuItem.Size = new System.Drawing.Size(109, 22); + this.noRecentsToolStripMenuItem.Text = "(none)"; + // + // toolStripMenuItem10 + // + this.toolStripMenuItem10.Name = "toolStripMenuItem10"; + this.toolStripMenuItem10.Size = new System.Drawing.Size(205, 6); + // + // exitToolStripMenuItem + // + this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; + this.exitToolStripMenuItem.Size = new System.Drawing.Size(208, 22); + this.exitToolStripMenuItem.Text = "Exit"; + this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click); + // + // editToolStripMenuItem + // + this.editToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.undoToolStripMenuItem, + this.redoToolStripMenuItem, + this.toolStripMenuItem1, + this.copyToolStripMenuItem, + this.toolStripMenuItem2, + this.selectAllToolStripMenuItem, + this.findToolStripMenuItem, + this.findNextToolStripMenuItem, + this.gotoToolStripMenuItem, + this.toolStripMenuItem3, + this.editHeaderCommentToolStripMenuItem, + this.projectPropertiesToolStripMenuItem, + this.toolStripMenuItem7, + this.settingsToolStripMenuItem}); + this.editToolStripMenuItem.Name = "editToolStripMenuItem"; + this.editToolStripMenuItem.Size = new System.Drawing.Size(39, 20); + this.editToolStripMenuItem.Text = "Edit"; + // + // undoToolStripMenuItem + // + this.undoToolStripMenuItem.Name = "undoToolStripMenuItem"; + this.undoToolStripMenuItem.ShortcutKeyDisplayString = ""; + this.undoToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Z))); + this.undoToolStripMenuItem.Size = new System.Drawing.Size(201, 22); + this.undoToolStripMenuItem.Text = "Undo"; + this.undoToolStripMenuItem.Click += new System.EventHandler(this.undoToolStripMenuItem_Click); + // + // redoToolStripMenuItem + // + this.redoToolStripMenuItem.Name = "redoToolStripMenuItem"; + this.redoToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Y))); + this.redoToolStripMenuItem.Size = new System.Drawing.Size(201, 22); + this.redoToolStripMenuItem.Text = "Redo"; + this.redoToolStripMenuItem.Click += new System.EventHandler(this.redoToolStripMenuItem_Click); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(198, 6); + // + // copyToolStripMenuItem + // + this.copyToolStripMenuItem.Name = "copyToolStripMenuItem"; + this.copyToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.C))); + this.copyToolStripMenuItem.Size = new System.Drawing.Size(201, 22); + this.copyToolStripMenuItem.Text = "Copy"; + this.copyToolStripMenuItem.Click += new System.EventHandler(this.copyToolStripMenuItem_Click); + // + // toolStripMenuItem2 + // + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + this.toolStripMenuItem2.Size = new System.Drawing.Size(198, 6); + // + // selectAllToolStripMenuItem + // + this.selectAllToolStripMenuItem.Name = "selectAllToolStripMenuItem"; + this.selectAllToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.A))); + this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(201, 22); + this.selectAllToolStripMenuItem.Text = "Select All"; + this.selectAllToolStripMenuItem.Click += new System.EventHandler(this.selectAllToolStripMenuItem_Click); + // + // findToolStripMenuItem + // + this.findToolStripMenuItem.Name = "findToolStripMenuItem"; + this.findToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.F))); + this.findToolStripMenuItem.Size = new System.Drawing.Size(201, 22); + this.findToolStripMenuItem.Text = "Find..."; + this.findToolStripMenuItem.Click += new System.EventHandler(this.findToolStripMenuItem_Click); + // + // findNextToolStripMenuItem + // + this.findNextToolStripMenuItem.Name = "findNextToolStripMenuItem"; + this.findNextToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F3; + this.findNextToolStripMenuItem.Size = new System.Drawing.Size(201, 22); + this.findNextToolStripMenuItem.Text = "Find Next"; + this.findNextToolStripMenuItem.Click += new System.EventHandler(this.findNextToolStripMenuItem_Click); + // + // gotoToolStripMenuItem + // + this.gotoToolStripMenuItem.Name = "gotoToolStripMenuItem"; + this.gotoToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.G))); + this.gotoToolStripMenuItem.Size = new System.Drawing.Size(201, 22); + this.gotoToolStripMenuItem.Text = "Go To..."; + this.gotoToolStripMenuItem.Click += new System.EventHandler(this.gotoToolStripMenuItem_Click); + // + // toolStripMenuItem3 + // + this.toolStripMenuItem3.Name = "toolStripMenuItem3"; + this.toolStripMenuItem3.Size = new System.Drawing.Size(198, 6); + // + // editHeaderCommentToolStripMenuItem + // + this.editHeaderCommentToolStripMenuItem.Name = "editHeaderCommentToolStripMenuItem"; + this.editHeaderCommentToolStripMenuItem.Size = new System.Drawing.Size(201, 22); + this.editHeaderCommentToolStripMenuItem.Text = "Edit Header Comment..."; + this.editHeaderCommentToolStripMenuItem.Click += new System.EventHandler(this.editHeaderCommentToolStripMenuItem_Click); + // + // projectPropertiesToolStripMenuItem + // + this.projectPropertiesToolStripMenuItem.Name = "projectPropertiesToolStripMenuItem"; + this.projectPropertiesToolStripMenuItem.Size = new System.Drawing.Size(201, 22); + this.projectPropertiesToolStripMenuItem.Text = "Project Properties..."; + this.projectPropertiesToolStripMenuItem.Click += new System.EventHandler(this.projectPropertiesToolStripMenuItem_Click); + // + // toolStripMenuItem7 + // + this.toolStripMenuItem7.Name = "toolStripMenuItem7"; + this.toolStripMenuItem7.Size = new System.Drawing.Size(198, 6); + // + // settingsToolStripMenuItem + // + this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem"; + this.settingsToolStripMenuItem.Size = new System.Drawing.Size(201, 22); + this.settingsToolStripMenuItem.Text = "Settings..."; + this.settingsToolStripMenuItem.Click += new System.EventHandler(this.settingsToolStripMenuItem_Click); + // + // actionsToolStripMenuItem + // + this.actionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.setAddressToolStripMenuItem, + this.overrideStatusFlagsToolStripMenuItem, + this.editLabelToolStripMenuItem, + this.editOperandToolStripMenuItem, + this.editDataFormatToolStripMenuItem, + this.editCommentToolStripMenuItem, + this.editLongCommentToolStripMenuItem, + this.editNoteToolStripMenuItem, + this.editProjectSymbolToolStripMenuItem, + this.actionsMenuSeparator1, + this.hintAsCodeToolStripMenuItem, + this.hintAsDataToolStripMenuItem, + this.hintAsInlineDataToolStripMenuItem, + this.removeHintToolStripMenuItem, + this.toolStripMenuItem11, + this.toggleSingleBytesToolStripMenuItem, + this.deleteNoteCommentToolStripMenuItem, + this.toolStripMenuItem9, + this.showHexDumpToolStripMenuItem}); + this.actionsToolStripMenuItem.Name = "actionsToolStripMenuItem"; + this.actionsToolStripMenuItem.Size = new System.Drawing.Size(59, 20); + this.actionsToolStripMenuItem.Text = "Actions"; + this.actionsToolStripMenuItem.DropDownOpening += new System.EventHandler(this.ActionsMenuOpening); + // + // setAddressToolStripMenuItem + // + this.setAddressToolStripMenuItem.Name = "setAddressToolStripMenuItem"; + this.setAddressToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.setAddressToolStripMenuItem.Text = "Set Address..."; + this.setAddressToolStripMenuItem.Click += new System.EventHandler(this.EditAddress_Click); + // + // overrideStatusFlagsToolStripMenuItem + // + this.overrideStatusFlagsToolStripMenuItem.Name = "overrideStatusFlagsToolStripMenuItem"; + this.overrideStatusFlagsToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.overrideStatusFlagsToolStripMenuItem.Text = "Override Status Flags..."; + this.overrideStatusFlagsToolStripMenuItem.Click += new System.EventHandler(this.EditStatusFlags_Click); + // + // editLabelToolStripMenuItem + // + this.editLabelToolStripMenuItem.Name = "editLabelToolStripMenuItem"; + this.editLabelToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.editLabelToolStripMenuItem.Text = "Edit Label..."; + this.editLabelToolStripMenuItem.Click += new System.EventHandler(this.EditLabel_Click); + // + // editOperandToolStripMenuItem + // + this.editOperandToolStripMenuItem.Name = "editOperandToolStripMenuItem"; + this.editOperandToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.editOperandToolStripMenuItem.Text = "Edit Operand..."; + this.editOperandToolStripMenuItem.Click += new System.EventHandler(this.EditOperand_Click); + // + // editDataFormatToolStripMenuItem + // + this.editDataFormatToolStripMenuItem.Name = "editDataFormatToolStripMenuItem"; + this.editDataFormatToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.editDataFormatToolStripMenuItem.Text = "Edit Data Format..."; + this.editDataFormatToolStripMenuItem.Click += new System.EventHandler(this.EditData_Click); + // + // editCommentToolStripMenuItem + // + this.editCommentToolStripMenuItem.Name = "editCommentToolStripMenuItem"; + this.editCommentToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.editCommentToolStripMenuItem.Text = "Edit Comment..."; + this.editCommentToolStripMenuItem.Click += new System.EventHandler(this.EditComment_Click); + // + // editLongCommentToolStripMenuItem + // + this.editLongCommentToolStripMenuItem.Name = "editLongCommentToolStripMenuItem"; + this.editLongCommentToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.editLongCommentToolStripMenuItem.Text = "Edit Long Comment..."; + this.editLongCommentToolStripMenuItem.Click += new System.EventHandler(this.EditLongComment_Click); + // + // editNoteToolStripMenuItem + // + this.editNoteToolStripMenuItem.Name = "editNoteToolStripMenuItem"; + this.editNoteToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.editNoteToolStripMenuItem.Text = "Edit Note..."; + this.editNoteToolStripMenuItem.Click += new System.EventHandler(this.EditNote_Click); + // + // editProjectSymbolToolStripMenuItem + // + this.editProjectSymbolToolStripMenuItem.Name = "editProjectSymbolToolStripMenuItem"; + this.editProjectSymbolToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.editProjectSymbolToolStripMenuItem.Text = "Edit Project Symbol..."; + this.editProjectSymbolToolStripMenuItem.Click += new System.EventHandler(this.EditProjectSymbol_Click); + // + // actionsMenuSeparator1 + // + this.actionsMenuSeparator1.Name = "actionsMenuSeparator1"; + this.actionsMenuSeparator1.Size = new System.Drawing.Size(252, 6); + // + // hintAsCodeToolStripMenuItem + // + this.hintAsCodeToolStripMenuItem.Name = "hintAsCodeToolStripMenuItem"; + this.hintAsCodeToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.hintAsCodeToolStripMenuItem.Text = "Hint As Code Entry Point"; + this.hintAsCodeToolStripMenuItem.Click += new System.EventHandler(this.MarkAsCode_Click); + // + // hintAsDataToolStripMenuItem + // + this.hintAsDataToolStripMenuItem.Name = "hintAsDataToolStripMenuItem"; + this.hintAsDataToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.hintAsDataToolStripMenuItem.Text = "Hint As Data Start"; + this.hintAsDataToolStripMenuItem.Click += new System.EventHandler(this.MarkAsData_Click); + // + // hintAsInlineDataToolStripMenuItem + // + this.hintAsInlineDataToolStripMenuItem.Name = "hintAsInlineDataToolStripMenuItem"; + this.hintAsInlineDataToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.hintAsInlineDataToolStripMenuItem.Text = "Hint As Inline Data"; + this.hintAsInlineDataToolStripMenuItem.Click += new System.EventHandler(this.MarkAsInlineData_Click); + // + // removeHintToolStripMenuItem + // + this.removeHintToolStripMenuItem.Name = "removeHintToolStripMenuItem"; + this.removeHintToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.removeHintToolStripMenuItem.Text = "Remove Hints"; + this.removeHintToolStripMenuItem.Click += new System.EventHandler(this.MarkAsNoHint_Click); + // + // toolStripMenuItem11 + // + this.toolStripMenuItem11.Name = "toolStripMenuItem11"; + this.toolStripMenuItem11.Size = new System.Drawing.Size(252, 6); + // + // toggleSingleBytesToolStripMenuItem + // + this.toggleSingleBytesToolStripMenuItem.Name = "toggleSingleBytesToolStripMenuItem"; + this.toggleSingleBytesToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.B))); + this.toggleSingleBytesToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.toggleSingleBytesToolStripMenuItem.Text = "Toggle Single-Byte Format"; + this.toggleSingleBytesToolStripMenuItem.Click += new System.EventHandler(this.ToggleSingleBytes_Click); + // + // deleteNoteCommentToolStripMenuItem + // + this.deleteNoteCommentToolStripMenuItem.Name = "deleteNoteCommentToolStripMenuItem"; + this.deleteNoteCommentToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Delete; + this.deleteNoteCommentToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.deleteNoteCommentToolStripMenuItem.Text = "Delete Note/Long Comment"; + this.deleteNoteCommentToolStripMenuItem.Click += new System.EventHandler(this.DeleteNoteComment_Click); + // + // toolStripMenuItem9 + // + this.toolStripMenuItem9.Name = "toolStripMenuItem9"; + this.toolStripMenuItem9.Size = new System.Drawing.Size(252, 6); + // + // showHexDumpToolStripMenuItem + // + this.showHexDumpToolStripMenuItem.Name = "showHexDumpToolStripMenuItem"; + this.showHexDumpToolStripMenuItem.Size = new System.Drawing.Size(255, 22); + this.showHexDumpToolStripMenuItem.Text = "Show Hex Dump"; + this.showHexDumpToolStripMenuItem.Click += new System.EventHandler(this.ShowHexDump_Click); + // + // toolsToolStripMenuItem + // + this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.hexDumpToolStripMenuItem, + this.aSCIIChartToolStripMenuItem}); + this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem"; + this.toolsToolStripMenuItem.Size = new System.Drawing.Size(47, 20); + this.toolsToolStripMenuItem.Text = "Tools"; + // + // hexDumpToolStripMenuItem + // + this.hexDumpToolStripMenuItem.Name = "hexDumpToolStripMenuItem"; + this.hexDumpToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.hexDumpToolStripMenuItem.Text = "Hex Dump..."; + this.hexDumpToolStripMenuItem.Click += new System.EventHandler(this.hexDumpToolStripMenuItem_Click); + // + // aSCIIChartToolStripMenuItem + // + this.aSCIIChartToolStripMenuItem.Name = "aSCIIChartToolStripMenuItem"; + this.aSCIIChartToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.aSCIIChartToolStripMenuItem.Text = "ASCII Chart"; + this.aSCIIChartToolStripMenuItem.Click += new System.EventHandler(this.aSCIIChartToolStripMenuItem_Click); + // + // helpToolStripMenuItem + // + this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.contentsToolStripMenuItem, + this.aboutToolStripMenuItem}); + this.helpToolStripMenuItem.Name = "helpToolStripMenuItem"; + this.helpToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + this.helpToolStripMenuItem.Text = "Help"; + // + // contentsToolStripMenuItem + // + this.contentsToolStripMenuItem.Name = "contentsToolStripMenuItem"; + this.contentsToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F1; + this.contentsToolStripMenuItem.Size = new System.Drawing.Size(155, 22); + this.contentsToolStripMenuItem.Text = "View Help..."; + this.contentsToolStripMenuItem.Click += new System.EventHandler(this.viewHelpToolStripMenuItem_Click); + // + // aboutToolStripMenuItem + // + this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; + this.aboutToolStripMenuItem.Size = new System.Drawing.Size(155, 22); + this.aboutToolStripMenuItem.Text = "About..."; + this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click); + // + // dEBUGToolStripMenuItem + // + this.dEBUGToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.reanalyzeToolStripMenuItem, + this.showUndoRedoHistoryToolStripMenuItem, + this.showAnalyzerOutputToolStripMenuItem, + this.showAnalysisTimersToolStripMenuItem, + this.extensionScriptInfoToolStripMenuItem, + this.toolStripMenuItem6, + this.toggleOwnerDrawToolStripMenuItem, + this.toggleCommentRulersToolStripMenuItem, + this.useKeepAliveHackToolStripMenuItem, + this.toolStripMenuItem8, + this.sourceGenTestsToolStripMenuItem}); + this.dEBUGToolStripMenuItem.Name = "dEBUGToolStripMenuItem"; + this.dEBUGToolStripMenuItem.Size = new System.Drawing.Size(56, 20); + this.dEBUGToolStripMenuItem.Text = "DEBUG"; + this.dEBUGToolStripMenuItem.DropDownOpened += new System.EventHandler(this.dEBUGToolStripMenuItem_DropDownOpened); + // + // reanalyzeToolStripMenuItem + // + this.reanalyzeToolStripMenuItem.Name = "reanalyzeToolStripMenuItem"; + this.reanalyzeToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F5; + this.reanalyzeToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + this.reanalyzeToolStripMenuItem.Text = "Re-analyze"; + this.reanalyzeToolStripMenuItem.Click += new System.EventHandler(this.reanalyzeToolStripMenuItem_Click); + // + // showUndoRedoHistoryToolStripMenuItem + // + this.showUndoRedoHistoryToolStripMenuItem.Name = "showUndoRedoHistoryToolStripMenuItem"; + this.showUndoRedoHistoryToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + this.showUndoRedoHistoryToolStripMenuItem.Text = "Show Undo/Redo History"; + this.showUndoRedoHistoryToolStripMenuItem.Click += new System.EventHandler(this.showUndoRedoHistoryToolStripMenuItem_Click); + // + // showAnalyzerOutputToolStripMenuItem + // + this.showAnalyzerOutputToolStripMenuItem.Name = "showAnalyzerOutputToolStripMenuItem"; + this.showAnalyzerOutputToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + this.showAnalyzerOutputToolStripMenuItem.Text = "Show Analyzer Output"; + this.showAnalyzerOutputToolStripMenuItem.Click += new System.EventHandler(this.showAnalyzerOutputToolStripMenuItem_Click); + // + // showAnalysisTimersToolStripMenuItem + // + this.showAnalysisTimersToolStripMenuItem.Name = "showAnalysisTimersToolStripMenuItem"; + this.showAnalysisTimersToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + this.showAnalysisTimersToolStripMenuItem.Text = "Show Analysis Timers"; + this.showAnalysisTimersToolStripMenuItem.Click += new System.EventHandler(this.showAnalysisTimersToolStripMenuItem_Click); + // + // extensionScriptInfoToolStripMenuItem + // + this.extensionScriptInfoToolStripMenuItem.Name = "extensionScriptInfoToolStripMenuItem"; + this.extensionScriptInfoToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + this.extensionScriptInfoToolStripMenuItem.Text = "Extension Script Info..."; + this.extensionScriptInfoToolStripMenuItem.Click += new System.EventHandler(this.extensionScriptInfoToolStripMenuItem_Click); + // + // toolStripMenuItem6 + // + this.toolStripMenuItem6.Name = "toolStripMenuItem6"; + this.toolStripMenuItem6.Size = new System.Drawing.Size(206, 6); + // + // toggleOwnerDrawToolStripMenuItem + // + this.toggleOwnerDrawToolStripMenuItem.Name = "toggleOwnerDrawToolStripMenuItem"; + this.toggleOwnerDrawToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + this.toggleOwnerDrawToolStripMenuItem.Text = "Toggle OwnerDraw"; + this.toggleOwnerDrawToolStripMenuItem.Click += new System.EventHandler(this.toggleOwnerDrawToolStripMenuItem_Click); + // + // toggleCommentRulersToolStripMenuItem + // + this.toggleCommentRulersToolStripMenuItem.Name = "toggleCommentRulersToolStripMenuItem"; + this.toggleCommentRulersToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + this.toggleCommentRulersToolStripMenuItem.Text = "Toggle Comment Rulers"; + this.toggleCommentRulersToolStripMenuItem.Click += new System.EventHandler(this.toggleCommentRulersToolStripMenuItem_Click); + // + // useKeepAliveHackToolStripMenuItem + // + this.useKeepAliveHackToolStripMenuItem.Name = "useKeepAliveHackToolStripMenuItem"; + this.useKeepAliveHackToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + this.useKeepAliveHackToolStripMenuItem.Text = "Use Keep-Alive Hack"; + this.useKeepAliveHackToolStripMenuItem.Click += new System.EventHandler(this.useKeepAliveHackToolStripMenuItem_Click); + // + // toolStripMenuItem8 + // + this.toolStripMenuItem8.Name = "toolStripMenuItem8"; + this.toolStripMenuItem8.Size = new System.Drawing.Size(206, 6); + // + // sourceGenTestsToolStripMenuItem + // + this.sourceGenTestsToolStripMenuItem.Name = "sourceGenTestsToolStripMenuItem"; + this.sourceGenTestsToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + this.sourceGenTestsToolStripMenuItem.Text = "Source Generation Tests..."; + this.sourceGenTestsToolStripMenuItem.Click += new System.EventHandler(this.sourceGenTestsToolStripMenuItem_Click); + // + // mainStatusStrip + // + this.mainStatusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripStatusLabel}); + this.mainStatusStrip.Location = new System.Drawing.Point(0, 592); + this.mainStatusStrip.Name = "mainStatusStrip"; + this.mainStatusStrip.Size = new System.Drawing.Size(805, 22); + this.mainStatusStrip.TabIndex = 1; + this.mainStatusStrip.Text = "Main StatusStrip"; + // + // toolStripStatusLabel + // + this.toolStripStatusLabel.Name = "toolStripStatusLabel"; + this.toolStripStatusLabel.Size = new System.Drawing.Size(39, 17); + this.toolStripStatusLabel.Text = "Ready"; + // + // mainToolStrip + // + this.mainToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.mainToolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.navigateBackToolStripButton, + this.navigateFwdToolStripButton, + this.toolStripSeparator2, + this.newToolStripButton, + this.openToolStripButton, + this.saveToolStripButton, + this.printToolStripButton, + this.toolStripSeparator, + this.cutToolStripButton, + this.copyToolStripButton, + this.pasteToolStripButton, + this.toolStripSeparator1, + this.helpToolStripButton}); + this.mainToolStrip.Location = new System.Drawing.Point(0, 24); + this.mainToolStrip.Name = "mainToolStrip"; + this.mainToolStrip.Size = new System.Drawing.Size(805, 25); + this.mainToolStrip.TabIndex = 2; + this.mainToolStrip.Text = "toolStrip1"; + // + // navigateBackToolStripButton + // + this.navigateBackToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.navigateBackToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("navigateBackToolStripButton.Image"))); + this.navigateBackToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.navigateBackToolStripButton.Name = "navigateBackToolStripButton"; + this.navigateBackToolStripButton.Size = new System.Drawing.Size(23, 22); + this.navigateBackToolStripButton.Text = "toolStripButton2"; + this.navigateBackToolStripButton.ToolTipText = "Navigate Backward"; + this.navigateBackToolStripButton.Click += new System.EventHandler(this.navigateBackToolStripButton_Click); + // + // navigateFwdToolStripButton + // + this.navigateFwdToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.navigateFwdToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("navigateFwdToolStripButton.Image"))); + this.navigateFwdToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.navigateFwdToolStripButton.Name = "navigateFwdToolStripButton"; + this.navigateFwdToolStripButton.Size = new System.Drawing.Size(23, 22); + this.navigateFwdToolStripButton.Text = "toolStripButton1"; + this.navigateFwdToolStripButton.ToolTipText = "Navigate Forward"; + this.navigateFwdToolStripButton.Click += new System.EventHandler(this.navigateFwdToolStripButton_Click); + // + // toolStripSeparator2 + // + this.toolStripSeparator2.Name = "toolStripSeparator2"; + this.toolStripSeparator2.Size = new System.Drawing.Size(6, 25); + // + // newToolStripButton + // + this.newToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.newToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("newToolStripButton.Image"))); + this.newToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.newToolStripButton.Name = "newToolStripButton"; + this.newToolStripButton.Size = new System.Drawing.Size(23, 22); + this.newToolStripButton.Text = "&New"; + this.newToolStripButton.Click += new System.EventHandler(this.newToolStripButton_Click); + // + // openToolStripButton + // + this.openToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.openToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("openToolStripButton.Image"))); + this.openToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.openToolStripButton.Name = "openToolStripButton"; + this.openToolStripButton.Size = new System.Drawing.Size(23, 22); + this.openToolStripButton.Text = "&Open"; + this.openToolStripButton.Click += new System.EventHandler(this.openToolStripButton_Click); + // + // saveToolStripButton + // + this.saveToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.saveToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("saveToolStripButton.Image"))); + this.saveToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.saveToolStripButton.Name = "saveToolStripButton"; + this.saveToolStripButton.Size = new System.Drawing.Size(23, 22); + this.saveToolStripButton.Text = "&Save"; + this.saveToolStripButton.Click += new System.EventHandler(this.saveToolStripButton_Click); + // + // printToolStripButton + // + this.printToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.printToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("printToolStripButton.Image"))); + this.printToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.printToolStripButton.Name = "printToolStripButton"; + this.printToolStripButton.Size = new System.Drawing.Size(23, 22); + this.printToolStripButton.Text = "&Print"; + this.printToolStripButton.Visible = false; + // + // toolStripSeparator + // + this.toolStripSeparator.Name = "toolStripSeparator"; + this.toolStripSeparator.Size = new System.Drawing.Size(6, 25); + // + // cutToolStripButton + // + this.cutToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.cutToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("cutToolStripButton.Image"))); + this.cutToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.cutToolStripButton.Name = "cutToolStripButton"; + this.cutToolStripButton.Size = new System.Drawing.Size(23, 22); + this.cutToolStripButton.Text = "C&ut"; + this.cutToolStripButton.Visible = false; + // + // copyToolStripButton + // + this.copyToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.copyToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("copyToolStripButton.Image"))); + this.copyToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.copyToolStripButton.Name = "copyToolStripButton"; + this.copyToolStripButton.Size = new System.Drawing.Size(23, 22); + this.copyToolStripButton.Text = "&Copy"; + // + // pasteToolStripButton + // + this.pasteToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.pasteToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("pasteToolStripButton.Image"))); + this.pasteToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.pasteToolStripButton.Name = "pasteToolStripButton"; + this.pasteToolStripButton.Size = new System.Drawing.Size(23, 22); + this.pasteToolStripButton.Text = "&Paste"; + this.pasteToolStripButton.Visible = false; + // + // toolStripSeparator1 + // + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25); + // + // helpToolStripButton + // + this.helpToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.helpToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("helpToolStripButton.Image"))); + this.helpToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.helpToolStripButton.Name = "helpToolStripButton"; + this.helpToolStripButton.Size = new System.Drawing.Size(23, 22); + this.helpToolStripButton.Text = "He&lp"; + this.helpToolStripButton.Click += new System.EventHandler(this.helpToolStripButton_Click); + // + // mainSplitterLeft + // + this.mainSplitterLeft.Dock = System.Windows.Forms.DockStyle.Fill; + this.mainSplitterLeft.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; + this.mainSplitterLeft.Location = new System.Drawing.Point(0, 49); + this.mainSplitterLeft.Name = "mainSplitterLeft"; + // + // mainSplitterLeft.Panel1 + // + this.mainSplitterLeft.Panel1.Controls.Add(this.leftPanelSplitter); + this.mainSplitterLeft.Panel1MinSize = 100; + // + // mainSplitterLeft.Panel2 + // + this.mainSplitterLeft.Panel2.Controls.Add(this.mainSplitterRight); + this.mainSplitterLeft.Panel2MinSize = 350; + this.mainSplitterLeft.Size = new System.Drawing.Size(805, 543); + this.mainSplitterLeft.SplitterDistance = 168; + this.mainSplitterLeft.TabIndex = 3; + // + // leftPanelSplitter + // + this.leftPanelSplitter.Dock = System.Windows.Forms.DockStyle.Fill; + this.leftPanelSplitter.Location = new System.Drawing.Point(0, 0); + this.leftPanelSplitter.Name = "leftPanelSplitter"; + this.leftPanelSplitter.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // leftPanelSplitter.Panel1 + // + this.leftPanelSplitter.Panel1.Controls.Add(this.referencesGroupBox); + this.leftPanelSplitter.Panel1MinSize = 100; + // + // leftPanelSplitter.Panel2 + // + this.leftPanelSplitter.Panel2.Controls.Add(this.notesGroupBox); + this.leftPanelSplitter.Panel2MinSize = 100; + this.leftPanelSplitter.Size = new System.Drawing.Size(168, 543); + this.leftPanelSplitter.SplitterDistance = 350; + this.leftPanelSplitter.TabIndex = 0; + this.leftPanelSplitter.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.SidePanelSplitter_SplitterMoved); + // + // referencesGroupBox + // + this.referencesGroupBox.Controls.Add(this.referencesListView); + this.referencesGroupBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.referencesGroupBox.Location = new System.Drawing.Point(0, 0); + this.referencesGroupBox.Name = "referencesGroupBox"; + this.referencesGroupBox.Size = new System.Drawing.Size(168, 350); + this.referencesGroupBox.TabIndex = 2; + this.referencesGroupBox.TabStop = false; + this.referencesGroupBox.Text = "References"; + // + // referencesListView + // + this.referencesListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.offsetColumnHeader, + this.addressColumnHeader, + this.typeColumnHeader}); + this.referencesListView.Dock = System.Windows.Forms.DockStyle.Fill; + this.referencesListView.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.referencesListView.FullRowSelect = true; + this.referencesListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.referencesListView.Location = new System.Drawing.Point(3, 16); + this.referencesListView.MultiSelect = false; + this.referencesListView.Name = "referencesListView"; + this.referencesListView.Size = new System.Drawing.Size(162, 331); + this.referencesListView.TabIndex = 1; + this.referencesListView.UseCompatibleStateImageBehavior = false; + this.referencesListView.View = System.Windows.Forms.View.Details; + this.referencesListView.ColumnWidthChanged += new System.Windows.Forms.ColumnWidthChangedEventHandler(this.referencesListView_ColumnWidthChanged); + this.referencesListView.SizeChanged += new System.EventHandler(this.referencesListView_SizeChanged); + this.referencesListView.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.referencesListView_MouseDoubleClick); + // + // offsetColumnHeader + // + this.offsetColumnHeader.Text = "Offset"; + this.offsetColumnHeader.Width = 52; + // + // addressColumnHeader + // + this.addressColumnHeader.Text = "Addr"; + this.addressColumnHeader.Width = 54; + // + // typeColumnHeader + // + this.typeColumnHeader.Text = "Type"; + // + // notesGroupBox + // + this.notesGroupBox.Controls.Add(this.notesListView); + this.notesGroupBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.notesGroupBox.Location = new System.Drawing.Point(0, 0); + this.notesGroupBox.Name = "notesGroupBox"; + this.notesGroupBox.Size = new System.Drawing.Size(168, 189); + this.notesGroupBox.TabIndex = 0; + this.notesGroupBox.TabStop = false; + this.notesGroupBox.Text = "Notes"; + // + // notesListView + // + this.notesListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.notesOffsetColumnHeader, + this.notesNoteColumnHeader}); + this.notesListView.Dock = System.Windows.Forms.DockStyle.Fill; + this.notesListView.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.notesListView.FullRowSelect = true; + this.notesListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.notesListView.Location = new System.Drawing.Point(3, 16); + this.notesListView.MultiSelect = false; + this.notesListView.Name = "notesListView"; + this.notesListView.OwnerDraw = true; + this.notesListView.Size = new System.Drawing.Size(162, 170); + this.notesListView.TabIndex = 0; + this.notesListView.UseCompatibleStateImageBehavior = false; + this.notesListView.View = System.Windows.Forms.View.Details; + this.notesListView.VirtualMode = true; + this.notesListView.CacheVirtualItems += new System.Windows.Forms.CacheVirtualItemsEventHandler(this.notesListView_CacheVirtualItems); + this.notesListView.ColumnWidthChanged += new System.Windows.Forms.ColumnWidthChangedEventHandler(this.notesListView_ColumnWidthChanged); + this.notesListView.DrawColumnHeader += new System.Windows.Forms.DrawListViewColumnHeaderEventHandler(this.notesListView_DrawColumnHeader); + this.notesListView.DrawItem += new System.Windows.Forms.DrawListViewItemEventHandler(this.notesListView_DrawItem); + this.notesListView.DrawSubItem += new System.Windows.Forms.DrawListViewSubItemEventHandler(this.notesListView_DrawSubItem); + this.notesListView.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.notesListView_RetrieveVirtualItem); + this.notesListView.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.notesListView_MouseDoubleClick); + // + // notesOffsetColumnHeader + // + this.notesOffsetColumnHeader.Text = "Offset"; + this.notesOffsetColumnHeader.Width = 52; + // + // notesNoteColumnHeader + // + this.notesNoteColumnHeader.Text = "Note"; + this.notesNoteColumnHeader.Width = 400; + // + // mainSplitterRight + // + this.mainSplitterRight.Dock = System.Windows.Forms.DockStyle.Fill; + this.mainSplitterRight.FixedPanel = System.Windows.Forms.FixedPanel.Panel2; + this.mainSplitterRight.Location = new System.Drawing.Point(0, 0); + this.mainSplitterRight.Name = "mainSplitterRight"; + // + // mainSplitterRight.Panel1 + // + this.mainSplitterRight.Panel1.Controls.Add(this.noProjectPanel); + this.mainSplitterRight.Panel1.Controls.Add(this.codeListView); + this.mainSplitterRight.Panel1MinSize = 150; + // + // mainSplitterRight.Panel2 + // + this.mainSplitterRight.Panel2.Controls.Add(this.rightPanelSplitter); + this.mainSplitterRight.Panel2MinSize = 100; + this.mainSplitterRight.Size = new System.Drawing.Size(633, 543); + this.mainSplitterRight.SplitterDistance = 446; + this.mainSplitterRight.TabIndex = 0; + // + // noProjectPanel + // + this.noProjectPanel.Controls.Add(this.debugModeEnabledLabel); + this.noProjectPanel.Controls.Add(this.recentProjectsLabel); + this.noProjectPanel.Controls.Add(this.recentProjectLabel2); + this.noProjectPanel.Controls.Add(this.versionLabel); + this.noProjectPanel.Controls.Add(this.sourceGenLabel); + this.noProjectPanel.Controls.Add(this.logoPictureBox); + this.noProjectPanel.Controls.Add(this.recentProjectLabel1); + this.noProjectPanel.Controls.Add(this.openExistingLabel); + this.noProjectPanel.Controls.Add(this.newProjectLink); + this.noProjectPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.noProjectPanel.Location = new System.Drawing.Point(0, 0); + this.noProjectPanel.Name = "noProjectPanel"; + this.noProjectPanel.Size = new System.Drawing.Size(446, 543); + this.noProjectPanel.TabIndex = 1; + // + // debugModeEnabledLabel + // + this.debugModeEnabledLabel.AutoSize = true; + this.debugModeEnabledLabel.Location = new System.Drawing.Point(219, 135); + this.debugModeEnabledLabel.Name = "debugModeEnabledLabel"; + this.debugModeEnabledLabel.Size = new System.Drawing.Size(271, 13); + this.debugModeEnabledLabel.TabIndex = 8; + this.debugModeEnabledLabel.Text = "Debug mode • extended logging and validation enabled"; + this.debugModeEnabledLabel.Visible = false; + // + // recentProjectsLabel + // + this.recentProjectsLabel.AutoSize = true; + this.recentProjectsLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.recentProjectsLabel.Location = new System.Drawing.Point(25, 307); + this.recentProjectsLabel.Name = "recentProjectsLabel"; + this.recentProjectsLabel.Size = new System.Drawing.Size(157, 20); + this.recentProjectsLabel.TabIndex = 7; + this.recentProjectsLabel.Text = "Most recent projects:"; + // + // recentProjectLabel2 + // + this.recentProjectLabel2.AutoSize = true; + this.recentProjectLabel2.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.recentProjectLabel2.Location = new System.Drawing.Point(40, 427); + this.recentProjectLabel2.Name = "recentProjectLabel2"; + this.recentProjectLabel2.Size = new System.Drawing.Size(135, 20); + this.recentProjectLabel2.TabIndex = 6; + this.recentProjectLabel2.TabStop = true; + this.recentProjectLabel2.Text = "Recent project #2"; + this.recentProjectLabel2.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.recentProjectLabel2_LinkClicked); + // + // versionLabel + // + this.versionLabel.AutoSize = true; + this.versionLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 20.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.versionLabel.Location = new System.Drawing.Point(213, 90); + this.versionLabel.Name = "versionLabel"; + this.versionLabel.Size = new System.Drawing.Size(273, 31); + this.versionLabel.TabIndex = 5; + this.versionLabel.Text = "Version X.Y.Z Alpha1"; + // + // sourceGenLabel + // + this.sourceGenLabel.AutoSize = true; + this.sourceGenLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 24F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.sourceGenLabel.Location = new System.Drawing.Point(213, 32); + this.sourceGenLabel.Name = "sourceGenLabel"; + this.sourceGenLabel.Size = new System.Drawing.Size(346, 37); + this.sourceGenLabel.TabIndex = 4; + this.sourceGenLabel.Text = "6502bench SourceGen"; + // + // logoPictureBox + // + this.logoPictureBox.ImageLocation = "SourceGen\\RuntimeData\\Logo.png"; + this.logoPictureBox.Location = new System.Drawing.Point(4, 4); + this.logoPictureBox.Name = "logoPictureBox"; + this.logoPictureBox.Size = new System.Drawing.Size(203, 145); + this.logoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.logoPictureBox.TabIndex = 3; + this.logoPictureBox.TabStop = false; + // + // recentProjectLabel1 + // + this.recentProjectLabel1.AutoSize = true; + this.recentProjectLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.recentProjectLabel1.Location = new System.Drawing.Point(40, 367); + this.recentProjectLabel1.Name = "recentProjectLabel1"; + this.recentProjectLabel1.Size = new System.Drawing.Size(135, 20); + this.recentProjectLabel1.TabIndex = 2; + this.recentProjectLabel1.TabStop = true; + this.recentProjectLabel1.Text = "Recent project #1"; + this.recentProjectLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.recentProjectLabel1_LinkClicked); + // + // openExistingLabel + // + this.openExistingLabel.AutoSize = true; + this.openExistingLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.openExistingLabel.Location = new System.Drawing.Point(25, 247); + this.openExistingLabel.Name = "openExistingLabel"; + this.openExistingLabel.Size = new System.Drawing.Size(157, 20); + this.openExistingLabel.TabIndex = 1; + this.openExistingLabel.TabStop = true; + this.openExistingLabel.Text = "Open existing project"; + this.openExistingLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.openExistingLabel_LinkClicked); + // + // newProjectLink + // + this.newProjectLink.AutoSize = true; + this.newProjectLink.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.newProjectLink.Location = new System.Drawing.Point(25, 187); + this.newProjectLink.Name = "newProjectLink"; + this.newProjectLink.Size = new System.Drawing.Size(129, 20); + this.newProjectLink.TabIndex = 0; + this.newProjectLink.TabStop = true; + this.newProjectLink.Text = "Start new project"; + this.newProjectLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.newFileLink_LinkClicked); + // + // codeListView + // + this.codeListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnOffset, + this.columnAddr, + this.columnBytes, + this.columnFlags, + this.columnAttr, + this.columnLabel, + this.columnOpcode, + this.columnOperand, + this.columnComment}); + this.codeListView.Dock = System.Windows.Forms.DockStyle.Fill; + this.codeListView.Font = new System.Drawing.Font("Consolas", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.codeListView.FullRowSelect = true; + this.codeListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.codeListView.HideSelection = false; + this.codeListView.Location = new System.Drawing.Point(0, 0); + this.codeListView.Name = "codeListView"; + this.codeListView.OwnerDraw = true; + this.codeListView.Size = new System.Drawing.Size(446, 543); + this.codeListView.TabIndex = 0; + this.codeListView.UseCompatibleStateImageBehavior = false; + this.codeListView.VirtualMode = true; + this.codeListView.CacheVirtualItems += new System.Windows.Forms.CacheVirtualItemsEventHandler(this.codeListView_CacheVirtualItems); + this.codeListView.ColumnWidthChanged += new System.Windows.Forms.ColumnWidthChangedEventHandler(this.codeListView_ColumnWidthChanged); + this.codeListView.DrawColumnHeader += new System.Windows.Forms.DrawListViewColumnHeaderEventHandler(this.codeListView_DrawColumnHeader); + this.codeListView.DrawItem += new System.Windows.Forms.DrawListViewItemEventHandler(this.codeListView_DrawItem); + this.codeListView.DrawSubItem += new System.Windows.Forms.DrawListViewSubItemEventHandler(this.codeListView_DrawSubItem); + this.codeListView.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.codeListView_ItemSelectionChanged); + this.codeListView.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.codeListView_RetrieveVirtualItem); + this.codeListView.SelectedIndexChanged += new System.EventHandler(this.codeListView_SelectedIndexChanged); + this.codeListView.VirtualItemsSelectionRangeChanged += new System.Windows.Forms.ListViewVirtualItemsSelectionRangeChangedEventHandler(this.codeListView_VirtualItemsSelectionRangeChanged); + this.codeListView.MouseClick += new System.Windows.Forms.MouseEventHandler(this.codeListView_MouseClick); + this.codeListView.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.codeListView_MouseDoubleClick); + // + // columnOffset + // + this.columnOffset.Text = "offset"; + this.columnOffset.Width = -2; + // + // columnAddr + // + this.columnAddr.Text = "addr"; + this.columnAddr.Width = -2; + // + // columnBytes + // + this.columnBytes.Text = "bytes"; + this.columnBytes.Width = -2; + // + // columnFlags + // + this.columnFlags.Text = "flags"; + this.columnFlags.Width = -2; + // + // columnAttr + // + this.columnAttr.Text = "attr"; + this.columnAttr.Width = -2; + // + // columnLabel + // + this.columnLabel.Text = "label"; + this.columnLabel.Width = -2; + // + // columnOpcode + // + this.columnOpcode.Text = "opcode"; + this.columnOpcode.Width = -2; + // + // columnOperand + // + this.columnOperand.Text = "operand"; + this.columnOperand.Width = -2; + // + // columnComment + // + this.columnComment.Text = "comment"; + this.columnComment.Width = -2; + // + // rightPanelSplitter + // + this.rightPanelSplitter.Dock = System.Windows.Forms.DockStyle.Fill; + this.rightPanelSplitter.FixedPanel = System.Windows.Forms.FixedPanel.Panel2; + this.rightPanelSplitter.Location = new System.Drawing.Point(0, 0); + this.rightPanelSplitter.Name = "rightPanelSplitter"; + this.rightPanelSplitter.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // rightPanelSplitter.Panel1 + // + this.rightPanelSplitter.Panel1.Controls.Add(this.symbolsGroupBox); + this.rightPanelSplitter.Panel1MinSize = 200; + // + // rightPanelSplitter.Panel2 + // + this.rightPanelSplitter.Panel2.Controls.Add(this.infoGroupBox); + this.rightPanelSplitter.Panel2MinSize = 100; + this.rightPanelSplitter.Size = new System.Drawing.Size(183, 543); + this.rightPanelSplitter.SplitterDistance = 400; + this.rightPanelSplitter.TabIndex = 8; + this.rightPanelSplitter.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.SidePanelSplitter_SplitterMoved); + // + // symbolsGroupBox + // + this.symbolsGroupBox.Controls.Add(this.symbolUserCheckBox); + this.symbolsGroupBox.Controls.Add(this.symbolProjectCheckBox); + this.symbolsGroupBox.Controls.Add(this.symbolAddressCheckBox); + this.symbolsGroupBox.Controls.Add(this.symbolConstantCheckBox); + this.symbolsGroupBox.Controls.Add(this.symbolAutoCheckBox); + this.symbolsGroupBox.Controls.Add(this.symbolPlatformCheckBox); + this.symbolsGroupBox.Controls.Add(this.symbolListView); + this.symbolsGroupBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.symbolsGroupBox.Location = new System.Drawing.Point(0, 0); + this.symbolsGroupBox.Name = "symbolsGroupBox"; + this.symbolsGroupBox.Size = new System.Drawing.Size(183, 400); + this.symbolsGroupBox.TabIndex = 2; + this.symbolsGroupBox.TabStop = false; + this.symbolsGroupBox.Text = "Symbols"; + // + // symbolUserCheckBox + // + this.symbolUserCheckBox.Appearance = System.Windows.Forms.Appearance.Button; + this.symbolUserCheckBox.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.symbolUserCheckBox.Location = new System.Drawing.Point(6, 19); + this.symbolUserCheckBox.Name = "symbolUserCheckBox"; + this.symbolUserCheckBox.Size = new System.Drawing.Size(39, 23); + this.symbolUserCheckBox.TabIndex = 7; + this.symbolUserCheckBox.Text = "User"; + this.symbolUserCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.symbolUserCheckBox.UseVisualStyleBackColor = true; + this.symbolUserCheckBox.CheckedChanged += new System.EventHandler(this.symbolUserCheckBox_CheckedChanged); + // + // symbolProjectCheckBox + // + this.symbolProjectCheckBox.Appearance = System.Windows.Forms.Appearance.Button; + this.symbolProjectCheckBox.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.symbolProjectCheckBox.Location = new System.Drawing.Point(51, 19); + this.symbolProjectCheckBox.Name = "symbolProjectCheckBox"; + this.symbolProjectCheckBox.Size = new System.Drawing.Size(39, 23); + this.symbolProjectCheckBox.TabIndex = 6; + this.symbolProjectCheckBox.Text = "Proj"; + this.symbolProjectCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.symbolProjectCheckBox.UseVisualStyleBackColor = true; + this.symbolProjectCheckBox.CheckedChanged += new System.EventHandler(this.symbolProjectCheckBox_CheckedChanged); + // + // symbolAddressCheckBox + // + this.symbolAddressCheckBox.Appearance = System.Windows.Forms.Appearance.Button; + this.symbolAddressCheckBox.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.symbolAddressCheckBox.Location = new System.Drawing.Point(5, 48); + this.symbolAddressCheckBox.Name = "symbolAddressCheckBox"; + this.symbolAddressCheckBox.Size = new System.Drawing.Size(39, 23); + this.symbolAddressCheckBox.TabIndex = 5; + this.symbolAddressCheckBox.Text = "Addr"; + this.symbolAddressCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.symbolAddressCheckBox.UseVisualStyleBackColor = true; + this.symbolAddressCheckBox.CheckedChanged += new System.EventHandler(this.symbolAddrCheckBox_CheckedChanged); + // + // symbolConstantCheckBox + // + this.symbolConstantCheckBox.Appearance = System.Windows.Forms.Appearance.Button; + this.symbolConstantCheckBox.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.symbolConstantCheckBox.Location = new System.Drawing.Point(51, 48); + this.symbolConstantCheckBox.Name = "symbolConstantCheckBox"; + this.symbolConstantCheckBox.Size = new System.Drawing.Size(39, 23); + this.symbolConstantCheckBox.TabIndex = 4; + this.symbolConstantCheckBox.Text = "Cnst"; + this.symbolConstantCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.symbolConstantCheckBox.UseVisualStyleBackColor = true; + this.symbolConstantCheckBox.CheckedChanged += new System.EventHandler(this.symbolConstantCheckBox_CheckedChanged); + // + // symbolAutoCheckBox + // + this.symbolAutoCheckBox.Appearance = System.Windows.Forms.Appearance.Button; + this.symbolAutoCheckBox.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.symbolAutoCheckBox.Location = new System.Drawing.Point(141, 19); + this.symbolAutoCheckBox.Name = "symbolAutoCheckBox"; + this.symbolAutoCheckBox.Size = new System.Drawing.Size(39, 23); + this.symbolAutoCheckBox.TabIndex = 1; + this.symbolAutoCheckBox.Text = "Auto"; + this.symbolAutoCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.symbolAutoCheckBox.UseVisualStyleBackColor = true; + this.symbolAutoCheckBox.CheckedChanged += new System.EventHandler(this.symbolAutoCheckBox_CheckedChanged); + // + // symbolPlatformCheckBox + // + this.symbolPlatformCheckBox.Appearance = System.Windows.Forms.Appearance.Button; + this.symbolPlatformCheckBox.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.symbolPlatformCheckBox.Location = new System.Drawing.Point(96, 19); + this.symbolPlatformCheckBox.Name = "symbolPlatformCheckBox"; + this.symbolPlatformCheckBox.Size = new System.Drawing.Size(39, 23); + this.symbolPlatformCheckBox.TabIndex = 3; + this.symbolPlatformCheckBox.Text = "Plat"; + this.symbolPlatformCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.symbolPlatformCheckBox.UseVisualStyleBackColor = true; + this.symbolPlatformCheckBox.CheckedChanged += new System.EventHandler(this.symbolPlatformCheckBox_CheckedChanged); + // + // symbolListView + // + this.symbolListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.symbolListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.symbolTypeColumnHeader, + this.symbolNameColumnHeader, + this.symbolValueColumnHeader}); + this.symbolListView.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.symbolListView.FullRowSelect = true; + this.symbolListView.GridLines = true; + this.symbolListView.Location = new System.Drawing.Point(3, 79); + this.symbolListView.MultiSelect = false; + this.symbolListView.Name = "symbolListView"; + this.symbolListView.Size = new System.Drawing.Size(177, 318); + this.symbolListView.TabIndex = 2; + this.symbolListView.UseCompatibleStateImageBehavior = false; + this.symbolListView.View = System.Windows.Forms.View.Details; + this.symbolListView.VirtualMode = true; + this.symbolListView.CacheVirtualItems += new System.Windows.Forms.CacheVirtualItemsEventHandler(this.symbolListView_CacheVirtualItems); + this.symbolListView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.symbolListView_ColumnClick); + this.symbolListView.ColumnWidthChanged += new System.Windows.Forms.ColumnWidthChangedEventHandler(this.symbolListView_ColumnWidthChanged); + this.symbolListView.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.symbolListView_RetrieveVirtualItem); + this.symbolListView.SizeChanged += new System.EventHandler(this.symbolListView_SizeChanged); + this.symbolListView.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.symbolListView_MouseDoubleClick); + // + // symbolTypeColumnHeader + // + this.symbolTypeColumnHeader.Text = "Type"; + this.symbolTypeColumnHeader.Width = 44; + // + // symbolNameColumnHeader + // + this.symbolNameColumnHeader.Text = "Name"; + // + // symbolValueColumnHeader + // + this.symbolValueColumnHeader.Text = "Value"; + // + // infoGroupBox + // + this.infoGroupBox.Controls.Add(this.infoTextBox); + this.infoGroupBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.infoGroupBox.Location = new System.Drawing.Point(0, 0); + this.infoGroupBox.Name = "infoGroupBox"; + this.infoGroupBox.Size = new System.Drawing.Size(183, 139); + this.infoGroupBox.TabIndex = 0; + this.infoGroupBox.TabStop = false; + this.infoGroupBox.Text = "Info"; + // + // infoTextBox + // + this.infoTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.infoTextBox.Location = new System.Drawing.Point(3, 16); + this.infoTextBox.Multiline = true; + this.infoTextBox.Name = "infoTextBox"; + this.infoTextBox.ReadOnly = true; + this.infoTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.infoTextBox.Size = new System.Drawing.Size(177, 120); + this.infoTextBox.TabIndex = 0; + // + // ProjectView + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(805, 614); + this.Controls.Add(this.mainSplitterLeft); + this.Controls.Add(this.mainStatusStrip); + this.Controls.Add(this.mainToolStrip); + this.Controls.Add(this.mainMenuStrip); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MinimumSize = new System.Drawing.Size(800, 600); + this.Name = "ProjectView"; + this.Text = "6502bench SourceGen"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ProjectView_FormClosing); + this.Load += new System.EventHandler(this.ProjectView_Load); + this.SizeChanged += new System.EventHandler(this.ProjectView_SizeChanged); + this.mainMenuStrip.ResumeLayout(false); + this.mainMenuStrip.PerformLayout(); + this.mainStatusStrip.ResumeLayout(false); + this.mainStatusStrip.PerformLayout(); + this.mainToolStrip.ResumeLayout(false); + this.mainToolStrip.PerformLayout(); + this.mainSplitterLeft.Panel1.ResumeLayout(false); + this.mainSplitterLeft.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.mainSplitterLeft)).EndInit(); + this.mainSplitterLeft.ResumeLayout(false); + this.leftPanelSplitter.Panel1.ResumeLayout(false); + this.leftPanelSplitter.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.leftPanelSplitter)).EndInit(); + this.leftPanelSplitter.ResumeLayout(false); + this.referencesGroupBox.ResumeLayout(false); + this.notesGroupBox.ResumeLayout(false); + this.mainSplitterRight.Panel1.ResumeLayout(false); + this.mainSplitterRight.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.mainSplitterRight)).EndInit(); + this.mainSplitterRight.ResumeLayout(false); + this.noProjectPanel.ResumeLayout(false); + this.noProjectPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).EndInit(); + this.rightPanelSplitter.Panel1.ResumeLayout(false); + this.rightPanelSplitter.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.rightPanelSplitter)).EndInit(); + this.rightPanelSplitter.ResumeLayout(false); + this.symbolsGroupBox.ResumeLayout(false); + this.infoGroupBox.ResumeLayout(false); + this.infoGroupBox.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.MenuStrip mainMenuStrip; + private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem openToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem editToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem copyToolStripMenuItem; + private System.Windows.Forms.StatusStrip mainStatusStrip; + private System.Windows.Forms.ToolStrip mainToolStrip; + private System.Windows.Forms.ToolStripButton newToolStripButton; + private System.Windows.Forms.ToolStripButton openToolStripButton; + private System.Windows.Forms.ToolStripButton saveToolStripButton; + private System.Windows.Forms.ToolStripButton printToolStripButton; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator; + private System.Windows.Forms.ToolStripButton cutToolStripButton; + private System.Windows.Forms.ToolStripButton copyToolStripButton; + private System.Windows.Forms.ToolStripButton pasteToolStripButton; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + private System.Windows.Forms.ToolStripButton helpToolStripButton; + private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel; + private System.Windows.Forms.SplitContainer mainSplitterLeft; + private System.Windows.Forms.SplitContainer mainSplitterRight; + private System.Windows.Forms.ListView codeListView; + private System.Windows.Forms.SplitContainer leftPanelSplitter; + private System.Windows.Forms.ListView referencesListView; + private System.Windows.Forms.Panel noProjectPanel; + private System.Windows.Forms.LinkLabel newProjectLink; + private System.Windows.Forms.LinkLabel openExistingLabel; + private System.Windows.Forms.LinkLabel recentProjectLabel1; + private System.Windows.Forms.ColumnHeader columnOffset; + private System.Windows.Forms.ColumnHeader columnAddr; + private System.Windows.Forms.ColumnHeader columnBytes; + private System.Windows.Forms.ColumnHeader columnFlags; + private System.Windows.Forms.ColumnHeader columnAttr; + private System.Windows.Forms.ColumnHeader columnLabel; + private System.Windows.Forms.ColumnHeader columnOpcode; + private System.Windows.Forms.ColumnHeader columnOperand; + private System.Windows.Forms.ColumnHeader columnComment; + private System.Windows.Forms.ToolStripMenuItem undoToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem redoToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + private System.Windows.Forms.CheckBox symbolAutoCheckBox; + private System.Windows.Forms.GroupBox symbolsGroupBox; + private System.Windows.Forms.ListView symbolListView; + private System.Windows.Forms.ColumnHeader symbolNameColumnHeader; + private System.Windows.Forms.ColumnHeader symbolValueColumnHeader; + private System.Windows.Forms.ColumnHeader symbolTypeColumnHeader; + private System.Windows.Forms.ColumnHeader offsetColumnHeader; + private System.Windows.Forms.ColumnHeader addressColumnHeader; + private System.Windows.Forms.ColumnHeader typeColumnHeader; + private System.Windows.Forms.GroupBox referencesGroupBox; + private System.Windows.Forms.GroupBox infoGroupBox; + private System.Windows.Forms.CheckBox symbolAddressCheckBox; + private System.Windows.Forms.CheckBox symbolConstantCheckBox; + private System.Windows.Forms.CheckBox symbolPlatformCheckBox; + private System.Windows.Forms.CheckBox symbolProjectCheckBox; + private System.Windows.Forms.CheckBox symbolUserCheckBox; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; + private System.Windows.Forms.ToolStripMenuItem selectAllToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem actionsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem setAddressToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem overrideStatusFlagsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem editLabelToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem editOperandToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem editDataFormatToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem editCommentToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem editLongCommentToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem editNoteToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator actionsMenuSeparator1; + private System.Windows.Forms.ToolStripMenuItem hintAsCodeToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem hintAsDataToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem hintAsInlineDataToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem removeHintToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem toolsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem dEBUGToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem toggleOwnerDrawToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem3; + private System.Windows.Forms.ToolStripMenuItem editHeaderCommentToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem saveToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem saveAsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem newToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem4; + private System.Windows.Forms.ToolStripMenuItem printToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem5; + private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; + private System.Windows.Forms.PictureBox logoPictureBox; + private System.Windows.Forms.Label versionLabel; + private System.Windows.Forms.Label sourceGenLabel; + private System.Windows.Forms.ToolStripMenuItem closeToolStripMenuItem; + private System.Windows.Forms.LinkLabel recentProjectLabel2; + private System.Windows.Forms.SplitContainer rightPanelSplitter; + private System.Windows.Forms.GroupBox notesGroupBox; + private System.Windows.Forms.ListView notesListView; + private System.Windows.Forms.ColumnHeader notesOffsetColumnHeader; + private System.Windows.Forms.ColumnHeader notesNoteColumnHeader; + private System.Windows.Forms.TextBox infoTextBox; + private System.Windows.Forms.ToolStripMenuItem reanalyzeToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem projectPropertiesToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem showUndoRedoHistoryToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem showAnalysisTimersToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem assembleToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem6; + private System.Windows.Forms.ToolStripMenuItem toggleCommentRulersToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem sourceGenTestsToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem8; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem7; + private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem extensionScriptInfoToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem showAnalyzerOutputToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem contentsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem hexDumpToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem9; + private System.Windows.Forms.ToolStripMenuItem showHexDumpToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem recentProjectsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem noRecentsToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem10; + private System.Windows.Forms.Label recentProjectsLabel; + private System.Windows.Forms.ToolStripMenuItem useKeepAliveHackToolStripMenuItem; + private System.Windows.Forms.Label debugModeEnabledLabel; + private System.Windows.Forms.ToolStripButton navigateFwdToolStripButton; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; + private System.Windows.Forms.ToolStripButton navigateBackToolStripButton; + private System.Windows.Forms.ToolStripMenuItem findToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem findNextToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem gotoToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem aSCIIChartToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem11; + private System.Windows.Forms.ToolStripMenuItem toggleSingleBytesToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem editProjectSymbolToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem deleteNoteCommentToolStripMenuItem; + } +} + diff --git a/SourceGen/AppForms/ProjectView.cs b/SourceGen/AppForms/ProjectView.cs new file mode 100644 index 0000000..850510a --- /dev/null +++ b/SourceGen/AppForms/ProjectView.cs @@ -0,0 +1,4357 @@ +/* + * Copyright 2018 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 System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Text; +using System.Web.Script.Serialization; +using System.Windows.Forms; + +using Asm65; +using CommonUtil; +using CommonWinForms; +using SourceGen.Sandbox; + +namespace SourceGen.AppForms { + /// + /// Main application form. This is the top-level application object. + /// + public partial class ProjectView : Form { + private const string LOGO_FILE_NAME = "Logo.png"; + private const string SETTINGS_FILE_NAME = "SourceGen-settings"; + + #region Project state + + // Currently open project, or null if none. + private DisasmProject mProject; + + // Pathname to 65xx data file. + private string mDataPathName; + + // Pathname of .dis65 file. This will be empty for a new project. + private string mProjectPathName; + + /// + /// Symbol subset, used to supply data to the symbol ListView. Initialized with + /// an empty symbol table. + /// + private SymbolTableSubset mSymbolSubset = new SymbolTableSubset(new SymbolTable()); + + /// + /// Current code list view selection. The length will match the DisplayList Count. + /// + /// A simple foreach through codeListView.SelectedIndices on a 500K-line data set + /// takes about 2.5 seconds on a fast Win10 x64 machine. Fortunately the control + /// notifies us of changes to the selection, so we can track it ourselves. + /// + private VirtualListViewSelection mCodeViewSelection = new VirtualListViewSelection(); + + /// + /// Data backing the codeListView. + /// + private DisplayList mDisplayList; + + #endregion Project state + + + /// + /// Returns the font currently in use by the code ListView. + /// + public Font CodeListViewFont { get { return codeListView.Font; } } + + /// + /// List of recently-opened projects. + /// + private List mRecentProjectPaths = new List(MAX_RECENT_PROJECTS); + private const int MAX_RECENT_PROJECTS = 6; + + /// + /// Menu items for the Actions and codeListView context menus. The menu items can + /// only be in one place at a time, so we move them around when the menu is opened. + /// + private ToolStripItem[] mActionsMenuItems; + + /// + /// Activity log generated by the code and data analyzers. Displayed in window. + /// + private DebugLog mGenerationLog; + + /// + /// Timing data generated during analysis. + /// + TaskTimer mReanalysisTimer = new TaskTimer(); + + /// + /// Base control to show when no project is open. + /// + private Control mNoProjectControl; + + /// + /// Base control to show when project is open. + /// + private Control mProjectControl; + + /// + /// Performance hack. + /// + private bool mRestoringSelection; + + /// + /// Stack for navigate forward/backward. + /// + private NavStack mNavStack = new NavStack(); + + /// + /// Output format configuration. + /// + private Formatter.FormatConfig mFormatterConfig; + + /// + /// Output format controller. + /// + /// This is shared with the DisplayList. + /// + private Formatter mOutputFormatter; + + /// + /// Pseudo-op names. + /// + /// This is shared with the DisplayList. + /// + private PseudoOp.PseudoOpNames mPseudoOpNames; + + /// + /// String we most recently searched for. + /// + private string mFindString = string.Empty; + + /// + /// Initial start point of most recent search. + /// + private int mFindStartIndex = -1; + + /// + /// Used to highlight the line that is the target of the selected line. + /// + private int mTargetHighlightIndex = -1; + + /// + /// CPU definition used when the Formatter was created. If the CPU choice or + /// inclusion of undocumented opcodes changes, we need to wipe the formatter. + /// + private CpuDef mOutputFormatterCpuDef; + + /// + /// Instruction description object. Used for Info window. + /// + private OpDescription mOpDesc = OpDescription.GetOpDescription(null); + + /// + /// If true, plugins will execute in the main application's AppDomain instead of + /// the sandbox. + /// + private bool mUseMainAppDomainForPlugins = false; + + /// + /// Project hex dump viewer. This is just for viewing the project contents, so this + /// dialog is tied to the project. + /// + /// The general-purpose file dump windows are independent (currently untracked). + /// + private Tools.HexDumpViewer mHexDumpDialog; + + /// + /// Floating ASCII chart dialog. Not tied to the project. + /// + private Tools.AsciiChart mAsciiChartDialog; + + + #region Init and settings + + public ProjectView() { + InitializeComponent(); + + ScriptManager.UseKeepAliveHack = true; + +#if DEBUG + debugModeEnabledLabel.Visible = true; +#endif + } + + private void ProjectView_Load(object sender, EventArgs e) { + if (RuntimeDataAccess.GetDirectory() == null) { + MessageBox.Show(Properties.Resources.RUNTIME_DIR_NOT_FOUND, + Properties.Resources.RUNTIME_DIR_NOT_FOUND_CAPTION, + MessageBoxButtons.OK, MessageBoxIcon.Error); + Application.Exit(); + } + try { + PluginDllCache.PreparePluginDir(); + } catch (Exception ex) { + string msg = string.Format(Properties.Resources.PLUGIN_DIR_FAIL, + PluginDllCache.GetPluginDirPath() + ": " + ex.Message); + MessageBox.Show(msg, Properties.Resources.PLUGIN_DIR_FAIL_CAPTION, + MessageBoxButtons.OK, MessageBoxIcon.Error); + Application.Exit(); + } + + logoPictureBox.ImageLocation = RuntimeDataAccess.GetPathName(LOGO_FILE_NAME); + versionLabel.Text = string.Format(Properties.Resources.VERSION_FMT, + Program.ProgramVersion); + + toolStripStatusLabel.Text = Properties.Resources.STATUS_READY; + + mProjectControl = this.codeListView; + mNoProjectControl = this.noProjectPanel; + + // Clone the menu structure from the designer. The same items are used for + // both Edit > Actions and the right-click context menu in codeListView. + mActionsMenuItems = new ToolStripItem[actionsToolStripMenuItem.DropDownItems.Count]; + for (int i = 0; i < actionsToolStripMenuItem.DropDownItems.Count; i++) { + mActionsMenuItems[i] = actionsToolStripMenuItem.DropDownItems[i]; + } + + // Init primary ListView (virtual, ownerdraw) + InitCodeListView(); + + // Init Symbols ListView (virtual, non-ownerdraw) + symbolListView.SetDoubleBuffered(true); + InitSymbolListView(); + + LoadAppSettings(); + ApplyAppSettings(); + + // Init References ListView (non-virtual, non-ownerdraw) + referencesListView.SetDoubleBuffered(true); + + UpdateActionMenu(); + UpdateMenuItemsAndTitle(); + UpdateRecentLinks(); + + ShowNoProject(); + } + + private void InitCodeListView() { + ListView cv = codeListView; + cv.View = View.Details; + + // Create an empty place-holder for the context menu. + codeListView.ContextMenuStrip = new ContextMenuStrip(); + + // When the Actions or context menu are opened, all menu items get transferred over. + codeListView.ContextMenuStrip.Opening += codeListView_CmsOpening; + + // Set default widths, in case we don't have a value for this in AppSettings. + CodeListColumnWidths widths = GetDefaultCodeListColumnWidths(); + SetCodeListHeaderWidths(widths); + + // This gets invoked when the user starts typing characters while the ListView + // has focus. Useful in a file viewer to find something by the first few + // characters. Less useful for us, though we could potentially use it as a + // shortcut to jump to an offset or label. + //codeListView.SearchForVirtualItem += + // new SearchForVirtualItemEventHandler(codeListView_SearchForVirtualItem); + + // Can't simply set this to "true", because the member is protected. + codeListView.SetDoubleBuffered(true); + } + + /// + /// Loads settings from the settings file into AppSettings.Global. Does not apply + /// them to the ProjectView. + /// + private void LoadAppSettings() { + AppSettings settings = AppSettings.Global; + + // Set some default settings for first-time use. The general rule is to set + // a default value of false, 0, or the empty string, so we only need to set + // values here when that isn't the case. The point at which the setting is + // retrieved is expected to do something reasonable by default. + + settings.SetBool(AppSettings.SYMWIN_SHOW_USER, true); + settings.SetBool(AppSettings.SYMWIN_SHOW_PROJECT, true); + settings.SetBool(AppSettings.SYMWIN_SHOW_PLATFORM, false); + settings.SetBool(AppSettings.SYMWIN_SHOW_AUTO, false); + settings.SetBool(AppSettings.SYMWIN_SHOW_CONST, true); + settings.SetBool(AppSettings.SYMWIN_SHOW_ADDR, true); + settings.SetBool(AppSettings.SYMWIN_SORT_ASCENDING, true); + settings.SetInt(AppSettings.SYMWIN_SORT_COL, (int)SymbolTableSubset.SortCol.Name); + + settings.SetBool(AppSettings.FMT_UPPER_OPERAND_A, true); + settings.SetBool(AppSettings.FMT_UPPER_OPERAND_S, true); + settings.SetBool(AppSettings.FMT_ADD_SPACE_FULL_COMMENT, true); + settings.SetString(AppSettings.FMT_OPCODE_SUFFIX_LONG, "l"); + settings.SetString(AppSettings.FMT_OPERAND_PREFIX_ABS, "a:"); + settings.SetString(AppSettings.FMT_OPERAND_PREFIX_LONG, "f:"); + + settings.SetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, true); + settings.SetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, true); + +#if DEBUG + settings.SetBool(AppSettings.DEBUG_MENU_ENABLED, true); +#else + settings.SetBool(AppSettings.DEBUG_MENU_ENABLED, false); +#endif + + // Load the settings file, and merge it into the globals. + string settingsDir = Path.GetDirectoryName(RuntimeDataAccess.GetDirectory()); + string settingsPath = Path.Combine(settingsDir, SETTINGS_FILE_NAME); + try { + string text = File.ReadAllText(settingsPath); + AppSettings fileSettings = AppSettings.Deserialize(text); + AppSettings.Global.MergeSettings(fileSettings); + Debug.WriteLine("Settings file loaded and merged"); + } catch (Exception ex) { + Debug.WriteLine("Unable to read settings file: " + ex.Message); + } + } + + /// + /// Saves AppSettings to a file. + /// + private void SaveAppSettings() { + if (!AppSettings.Global.Dirty) { + Debug.WriteLine("Settings not dirty, not saving"); + return; + } + + // Collect some window and column widths. Don't grab the window size if we're + // maximized or minimized. + if (this.WindowState == FormWindowState.Normal) { + AppSettings.Global.SetInt(AppSettings.MAIN_WINDOW_WIDTH, this.Size.Width); + AppSettings.Global.SetInt(AppSettings.MAIN_WINDOW_HEIGHT, this.Size.Height); + AppSettings.Global.SetInt(AppSettings.MAIN_LEFT_SPLITTER_DIST, + mainSplitterLeft.SplitterDistance); + AppSettings.Global.SetInt(AppSettings.MAIN_RIGHT_SPLITTER_DIST, + mainSplitterRight.SplitterDistance); + AppSettings.Global.SetInt(AppSettings.MAIN_LEFT_SIDE_SPLITTER_DIST, + leftPanelSplitter.SplitterDistance); + AppSettings.Global.SetInt(AppSettings.MAIN_RIGHT_SIDE_SPLITTER_DIST, + rightPanelSplitter.SplitterDistance); + } + SerializeReferencesColumnWidths(); + SerializeNotesColumnWidths(); + SerializeSymbolColumnWidths(); + + string settingsDir = Path.GetDirectoryName(RuntimeDataAccess.GetDirectory()); + string settingsPath = Path.Combine(settingsDir, SETTINGS_FILE_NAME); + try { + string cereal = AppSettings.Global.Serialize(); + File.WriteAllText(settingsPath, cereal); + AppSettings.Global.Dirty = false; + Debug.WriteLine("Saved settings (" + settingsPath + ")"); + } catch (Exception ex) { + Debug.WriteLine("Failed to save settings: " + ex.Message); + } + } + + /// + /// Replaces the contents of the global settings object with the new settings, + /// then applies them to the ProjectView. + /// + /// + public void SetAppSettings(AppSettings settings) { + AppSettings.Global.ReplaceSettings(settings); + ApplyAppSettings(); + + // We get called whenever Apply or OK is hit in the settings editor, so it's + // a pretty good time to save the settings out. + SaveAppSettings(); + } + + /// + /// Applies "actionable" settings to the ProjectView, pulling them out of the global + /// settings object. If a project is open, refreshes the display list and all sub-windows. + /// + private void ApplyAppSettings() { + Debug.WriteLine("ApplyAppSettings..."); + AppSettings settings = AppSettings.Global; + + // Main window size. + this.Size = new Size( + settings.GetInt(AppSettings.MAIN_WINDOW_WIDTH, 1280), + settings.GetInt(AppSettings.MAIN_WINDOW_HEIGHT, 720)); + // Left splitter with is distance from left edge of window. + mainSplitterLeft.SplitterDistance = + settings.GetInt(AppSettings.MAIN_LEFT_SPLITTER_DIST, 250); + // Right splitter posn is distance from right edge of left splitter. + mainSplitterRight.SplitterDistance = + settings.GetInt(AppSettings.MAIN_RIGHT_SPLITTER_DIST, (1280 - 250) - 250); + + leftPanelSplitter.SplitterDistance = + settings.GetInt(AppSettings.MAIN_LEFT_SIDE_SPLITTER_DIST, 350); + rightPanelSplitter.SplitterDistance = + settings.GetInt(AppSettings.MAIN_RIGHT_SIDE_SPLITTER_DIST, 400); + + // Configure column widths. + string widthStr = settings.GetString(AppSettings.CDLV_COL_WIDTHS, null); + if (!string.IsNullOrEmpty(widthStr)) { + CodeListColumnWidths widths = CodeListColumnWidths.Deserialize(widthStr); + if (widths != null) { + SetCodeListHeaderWidths(widths); + } + } + DeserializeReferencesColumnWidths(); + DeserializeNotesColumnWidths(); + DeserializeSymbolColumnWidths(); + + // Set up the formatter. + mFormatterConfig = new Formatter.FormatConfig(); + AsmGen.GenCommon.ConfigureFormatterFromSettings(AppSettings.Global, + ref mFormatterConfig); + mFormatterConfig.mEndOfLineCommentDelimiter = ";"; + mFormatterConfig.mFullLineCommentDelimiterBase = ";"; + mFormatterConfig.mBoxLineCommentDelimiter = string.Empty; + mFormatterConfig.mAllowHighAsciiCharConst = true; + mOutputFormatter = new Formatter(mFormatterConfig); + mOutputFormatterCpuDef = null; + + // Set pseudo-op names. Entries aren't allowed to be blank, so we start with the + // default values and merge in whatever the user has configured. + mPseudoOpNames = PseudoOp.sDefaultPseudoOpNames.GetCopy(); + string pseudoCereal = settings.GetString(AppSettings.FMT_PSEUDO_OP_NAMES, null); + if (!string.IsNullOrEmpty(pseudoCereal)) { + PseudoOp.PseudoOpNames deser = PseudoOp.PseudoOpNames.Deserialize(pseudoCereal); + if (deser != null) { + mPseudoOpNames.Merge(deser); + } + } + + // Configure the Symbols window. + symbolUserCheckBox.Checked = + settings.GetBool(AppSettings.SYMWIN_SHOW_USER, false); + symbolAutoCheckBox.Checked = + settings.GetBool(AppSettings.SYMWIN_SHOW_AUTO, false); + symbolProjectCheckBox.Checked = + settings.GetBool(AppSettings.SYMWIN_SHOW_PROJECT, false); + symbolPlatformCheckBox.Checked = + settings.GetBool(AppSettings.SYMWIN_SHOW_PLATFORM, false); + symbolConstantCheckBox.Checked = + settings.GetBool(AppSettings.SYMWIN_SHOW_CONST, false); + symbolAddressCheckBox.Checked = + settings.GetBool(AppSettings.SYMWIN_SHOW_ADDR, false); + + // Set the code list view font. + string fontStr = settings.GetString(AppSettings.CDLV_FONT, null); + if (!string.IsNullOrEmpty(fontStr)) { + FontConverter cvt = new FontConverter(); + try { + Font font = cvt.ConvertFromInvariantString(fontStr) as Font; + codeListView.Font = font; + Debug.WriteLine("Set font to " + font.ToString()); + } catch (Exception ex) { + Debug.WriteLine("Font convert failed: " + ex.Message); + } + } + + // Unpack the recent-project list. + UnpackRecentProjectList(); + + // Enable the DEBUG menu if configured. + bool showDebugMenu = AppSettings.Global.GetBool(AppSettings.DEBUG_MENU_ENABLED, false); + if (dEBUGToolStripMenuItem.Visible != showDebugMenu) { + dEBUGToolStripMenuItem.Visible = showDebugMenu; + mainMenuStrip.Refresh(); + } + + // Finally, update the display list with all the fancy settings. + if (mDisplayList != null) { + // Regenerate the display list with the latest formatter config and + // pseudo-op definition. (These are set as part of the refresh.) + UndoableChange uc = + UndoableChange.CreateDummyChange(UndoableChange.ReanalysisScope.DisplayOnly); + ApplyChanges(new ChangeSet(uc), false); + } + } + + // Make sure we pick up changes to the window size. We don't catch size-chage events + // for the left/right splitter widths because that should be picked up by the sub-windows. + private void ProjectView_SizeChanged(object sender, EventArgs e) { + AppSettings.Global.Dirty = true; + } + + // This handles the splitters in the side panels, e.g. between References and Notes. + private void SidePanelSplitter_SplitterMoved(object sender, SplitterEventArgs e) { + AppSettings.Global.Dirty = true; + } + + public void ShowProject() { + mProjectControl.Show(); + mNoProjectControl.Hide(); + + codeListView.Focus(); + saveToolStripMenuItem.Enabled = true; + saveToolStripButton.Enabled = true; + saveAsToolStripMenuItem.Enabled = true; + closeToolStripMenuItem.Enabled = true; + assembleToolStripMenuItem.Enabled = true; + printToolStripMenuItem.Enabled = true; + copyToolStripMenuItem.Enabled = true; + copyToolStripButton.Enabled = true; + selectAllToolStripMenuItem.Enabled = true; + findToolStripMenuItem.Enabled = true; + findNextToolStripMenuItem.Enabled = true; + gotoToolStripMenuItem.Enabled = true; + editHeaderCommentToolStripMenuItem.Enabled = true; + projectPropertiesToolStripMenuItem.Enabled = true; + + showUndoRedoHistoryToolStripMenuItem.Enabled = true; + showAnalysisTimersToolStripMenuItem.Enabled = true; + showAnalyzerOutputToolStripMenuItem.Enabled = true; + toggleOwnerDrawToolStripMenuItem.Enabled = true; + reanalyzeToolStripMenuItem.Enabled = true; + toggleCommentRulersToolStripMenuItem.Enabled = true; + extensionScriptInfoToolStripMenuItem.Enabled = true; + + mNavStack.Clear(); + UpdateMenuItemsAndTitle(); + } + + public void ShowNoProject() { + mProjectControl.Hide(); + mNoProjectControl.Show(); + + saveToolStripMenuItem.Enabled = false; + saveToolStripButton.Enabled = false; + saveAsToolStripMenuItem.Enabled = false; + closeToolStripMenuItem.Enabled = false; + assembleToolStripMenuItem.Enabled = false; + printToolStripMenuItem.Enabled = false; + copyToolStripMenuItem.Enabled = false; + copyToolStripButton.Enabled = false; + selectAllToolStripMenuItem.Enabled = false; + findToolStripMenuItem.Enabled = false; + findNextToolStripMenuItem.Enabled = false; + gotoToolStripMenuItem.Enabled = false; + editHeaderCommentToolStripMenuItem.Enabled = false; + projectPropertiesToolStripMenuItem.Enabled = false; + + showUndoRedoHistoryToolStripMenuItem.Enabled = false; + showAnalysisTimersToolStripMenuItem.Enabled = false; + showAnalyzerOutputToolStripMenuItem.Enabled = false; + toggleOwnerDrawToolStripMenuItem.Enabled = false; + reanalyzeToolStripMenuItem.Enabled = false; + toggleCommentRulersToolStripMenuItem.Enabled = false; + extensionScriptInfoToolStripMenuItem.Enabled = false; + + UpdateMenuItemsAndTitle(); + } + + private void UnpackRecentProjectList() { + mRecentProjectPaths.Clear(); + + string cereal = AppSettings.Global.GetString( + AppSettings.PRVW_RECENT_PROJECT_LIST, null); + if (string.IsNullOrEmpty(cereal)) { + return; + } + + try { + JavaScriptSerializer ser = new JavaScriptSerializer(); + mRecentProjectPaths = ser.Deserialize>(cereal); + } catch (Exception ex) { + Debug.WriteLine("Failed deserializing recent projects: " + ex.Message); + return; + } + } + + /// + /// Ensures that the named project is at the top of the list. If it's elsewhere + /// in the list, move it to the top. Excess items are removed. + /// + /// + private void UpdateRecentProjectList(string projectPath) { + if (string.IsNullOrEmpty(projectPath)) { + // This can happen if you create a new project, then close the window + // without having saved it. + return; + } + int index = mRecentProjectPaths.IndexOf(projectPath); + if (index == 0) { + // Already in the list, nothing changes. No need to update anything else. + return; + } + if (index > 0) { + mRecentProjectPaths.RemoveAt(index); + } + mRecentProjectPaths.Insert(0, projectPath); + + // Trim the list to the max allowed. + while (mRecentProjectPaths.Count > MAX_RECENT_PROJECTS) { + Debug.WriteLine("Recent projects: dropping " + + mRecentProjectPaths[MAX_RECENT_PROJECTS]); + mRecentProjectPaths.RemoveAt(MAX_RECENT_PROJECTS); + } + + // Store updated list in app settings. JSON-in-JSON is ugly and inefficient, + // but it'll do for now. + JavaScriptSerializer ser = new JavaScriptSerializer(); + string cereal = ser.Serialize(mRecentProjectPaths); + AppSettings.Global.SetString(AppSettings.PRVW_RECENT_PROJECT_LIST, cereal); + + UpdateRecentLinks(); + } + + /// + /// Updates the links on the no-project control. + /// + private void UpdateRecentLinks() { + if (mRecentProjectPaths.Count >= 1) { + recentProjectLabel1.Visible = true; + recentProjectLabel1.Text = string.Format(Properties.Resources.RECENT_PROJECT_LINK, + 1, Path.GetFileName(mRecentProjectPaths[0])); + } else { + recentProjectLabel1.Visible = false; + } + if (mRecentProjectPaths.Count >= 2) { + recentProjectLabel2.Visible = true; + recentProjectLabel2.Text = string.Format(Properties.Resources.RECENT_PROJECT_LINK, + 2, Path.GetFileName(mRecentProjectPaths[1])); + } else { + recentProjectLabel2.Visible = false; + } + } + + private void recentProjectsToolStripMenuItem_DropDownOpening(object sender, EventArgs e) { + ToolStripItemCollection recents = recentProjectsToolStripMenuItem.DropDownItems; + + recents.Clear(); + if (mRecentProjectPaths.Count == 0) { + recents.Add(noRecentsToolStripMenuItem); + } else { + for (int i = 0; i < mRecentProjectPaths.Count; i++) { + string pathName = mRecentProjectPaths[i]; + string menuName = string.Format("{0}: {1}", i + 1, + /*Path.GetFileName(*/ pathName /*)*/); + recents.Add(new ToolStripMenuItem(menuName, null, (sendr, arg) => { + if (DoClose()) { + DoOpenFile(pathName); + } + })); + } + } + } + + #endregion Init and settings + + + #region Project management + + private bool PrepareNewProject(string dataPathName, Setup.SystemDef sysDef) { + DisasmProject proj = new DisasmProject(); + mDataPathName = dataPathName; + mProjectPathName = string.Empty; + byte[] fileData = null; + try { + fileData = LoadDataFile(dataPathName); + } catch (Exception ex) { + Debug.WriteLine("PrepareNewProject exception: " + ex); + string message = Properties.Resources.OPEN_DATA_FAIL_CAPTION; + string caption = Properties.Resources.OPEN_DATA_FAIL_MESSAGE + ": " + ex.Message; + MessageBox.Show(caption, message, MessageBoxButtons.OK, MessageBoxIcon.Error); + return false; + } + proj.UseMainAppDomainForPlugins = mUseMainAppDomainForPlugins; + proj.Initialize(fileData.Length); + proj.PrepForNew(fileData, Path.GetFileName(dataPathName)); + + proj.LongComments.Add(DisplayList.Line.HEADER_COMMENT_OFFSET, + new MultiLineComment("6502bench SourceGen v" + Program.ProgramVersion)); + + // The system definition provides a set of defaults that can be overridden. + // We pull everything of interest out and then discard the object. + proj.ApplySystemDef(sysDef); + + mProject = proj; + + return true; + } + + private void FinishPrep() { + string messages = mProject.LoadExternalFiles(); + if (messages.Length != 0) { + // ProjectLoadIssues isn't quite the right dialog, but it'll do. + ProjectLoadIssues dlg = new ProjectLoadIssues(); + dlg.CanCancel = false; + dlg.Messages = messages; + dlg.ShowDialog(); + dlg.Dispose(); + } + + mDisplayList = new DisplayList(mProject, mOutputFormatter, mPseudoOpNames); + + // Prep the symbol table subset object. Replace the old one with a new one. + mSymbolSubset = new SymbolTableSubset(mProject.SymbolTable); + + RefreshProject(UndoableChange.ReanalysisScope.CodeAndData); + ShowProject(); + InvalidateControls(null); + + // Want to do this after ShowProject() or we see a weird glitch. + UpdateRecentProjectList(mProjectPathName); + } + + /// + /// Loads the data file, reading it entirely into memory. + /// + /// All errors are reported as exceptions. + /// + /// Full pathname. + /// Data file contents. + private byte[] LoadDataFile(string dataFileName) { + byte[] fileData; + + using (FileStream fs = File.Open(dataFileName, FileMode.Open, FileAccess.Read)) { + // Check length; should have been caught earlier. + if (fs.Length > DisasmProject.MAX_DATA_FILE_SIZE) { + throw new InvalidDataException( + string.Format(Properties.Resources.OPEN_DATA_TOO_LARGE, + fs.Length / 1024, DisasmProject.MAX_DATA_FILE_SIZE / 1024)); + } else if (fs.Length == 0) { + throw new InvalidDataException(Properties.Resources.OPEN_DATA_EMPTY); + } + fileData = new byte[fs.Length]; + int actual = fs.Read(fileData, 0, (int)fs.Length); + if (actual != fs.Length) { + // Not expected -- should be able to read the entire file in one shot. + throw new Exception(Properties.Resources.OPEN_DATA_PARTIAL_READ); + } + } + + return fileData; + } + + /// + /// Applies the changes to the project, adds them to the undo stack, and updates + /// the display. + /// + /// Set of changes to apply. + private void ApplyUndoableChanges(ChangeSet cs) { + if (cs.Count == 0) { + Debug.WriteLine("ApplyUndoableChanges: change set is empty"); + } + ApplyChanges(cs, false); + mProject.PushChangeSet(cs); + UpdateMenuItemsAndTitle(); + + // If the debug dialog is visible, update it. + if (mShowUndoRedoHistoryDialog != null) { + mShowUndoRedoHistoryDialog.BodyText = mProject.DebugGetUndoRedoHistory(); + } + } + + /// + /// Applies the changes to the project, and updates the display. + /// + /// This is called by the undo/redo commands. Don't call this directly from the + /// various UI-driven functions, as this does not add the change to the undo stack. + /// + /// Set of changes to apply. + /// If set, undo the changes instead. + private void ApplyChanges(ChangeSet cs, bool backward) { + mReanalysisTimer.Clear(); + mReanalysisTimer.StartTask("ProjectView.ApplyChanges()"); + + mReanalysisTimer.StartTask("Save selection"); + int topItem = codeListView.TopItem.Index; + int topOffset = mDisplayList[topItem].FileOffset; + DisplayList.SavedSelection savedSel = DisplayList.SavedSelection.Generate( + mDisplayList, mCodeViewSelection, topOffset); + //savedSel.DebugDump(); + mReanalysisTimer.EndTask("Save selection"); + + mReanalysisTimer.StartTask("Apply changes"); + UndoableChange.ReanalysisScope needReanalysis = mProject.ApplyChanges(cs, backward, + out RangeSet affectedOffsets); + mReanalysisTimer.EndTask("Apply changes"); + + string refreshTaskStr = "Refresh w/reanalysis=" + needReanalysis; + mReanalysisTimer.StartTask(refreshTaskStr); + if (needReanalysis != UndoableChange.ReanalysisScope.None) { + Debug.WriteLine("Refreshing project (" + needReanalysis + ")"); + RefreshProject(needReanalysis); + } else { + Debug.WriteLine("Refreshing " + affectedOffsets.Count + " offsets"); + RefreshCodeListViewEntries(affectedOffsets); + mProject.Validate(); // shouldn't matter w/o reanalysis, but do it anyway + } + mReanalysisTimer.EndTask(refreshTaskStr); + + VirtualListViewSelection newSel = savedSel.Restore(mDisplayList, out int topIndex); + //newSel.DebugDump(); + + // Refresh the various windows, and restore the selection. + mReanalysisTimer.StartTask("Invalidate controls"); + InvalidateControls(newSel); + mReanalysisTimer.EndTask("Invalidate controls"); + + // This apparently has to be done after the EndUpdate, and inside try/catch. + // See https://stackoverflow.com/questions/626315/ for notes. + try { + Debug.WriteLine("Setting TopItem to index=" + topIndex); + codeListView.TopItem = codeListView.Items[topIndex]; + } catch (NullReferenceException) { + Debug.WriteLine("Caught an NRE from TopItem"); + } + + mReanalysisTimer.EndTask("ProjectView.ApplyChanges()"); + + //mReanalysisTimer.DumpTimes("ProjectView timers:", mGenerationLog); + if (mShowAnalysisTimersDialog != null) { + string timerStr = mReanalysisTimer.DumpToString("ProjectView timers:"); + mShowAnalysisTimersDialog.BodyText = timerStr; + } + } + + /// + /// Refreshes the project after something of substance has changed. Some + /// re-analysis will be done, followed by a complete rebuild of the DisplayList. + /// + /// Indicates whether reanalysis is required, and + /// what level. + private void RefreshProject(UndoableChange.ReanalysisScope reanalysisRequired) { + Debug.Assert(reanalysisRequired != UndoableChange.ReanalysisScope.None); + + // NOTE: my goal is to arrange things so that reanalysis (data-only, and ideally + // code+data) takes less than 100ms. With that response time there's no need for + // background processing and progress bars. Since we need to do data-only + // reanalysis after many common operations, the program becomes unpleasant to + // use if we miss this goal, and progress bars won't make it less so. + + // Changing the CPU type or whether undocumented instructions are supported + // invalidates the Formatter's mnemonic cache. We can change these values + // through undo/redo, so we need to check it here. + if (mOutputFormatterCpuDef != mProject.CpuDef) { // reference equality is fine + Debug.WriteLine("CpuDef has changed, resetting formatter (now " + + mProject.CpuDef + ")"); + mOutputFormatter = new Formatter(mFormatterConfig); + mDisplayList.SetFormatter(mOutputFormatter); + mDisplayList.SetPseudoOpNames(mPseudoOpNames); + mOutputFormatterCpuDef = mProject.CpuDef; + } + + if (mDisplayList.Count > 200000) { + string prevStatus = toolStripStatusLabel.Text; + + // The Windows stuff can take 50-100ms, potentially longer than the actual + // work, so don't bother unless the file is very large. + try { + mReanalysisTimer.StartTask("Do Windows stuff"); + Application.UseWaitCursor = true; + Cursor.Current = Cursors.WaitCursor; + toolStripStatusLabel.Text = Properties.Resources.STATUS_RECALCULATING; + Refresh(); // redraw status label + mReanalysisTimer.EndTask("Do Windows stuff"); + + DoRefreshProject(reanalysisRequired); + } finally { + Application.UseWaitCursor = false; + toolStripStatusLabel.Text = prevStatus; + } + } else { + DoRefreshProject(reanalysisRequired); + } + + if (FormatDescriptor.DebugCreateCount != 0) { + Debug.WriteLine("FormatDescriptor total=" + FormatDescriptor.DebugCreateCount + + " prefab=" + FormatDescriptor.DebugPrefabCount + " (" + + (FormatDescriptor.DebugPrefabCount * 100) / FormatDescriptor.DebugCreateCount + + "%)"); + } + } + + /// + /// Updates all of the specified ListView entries. This is called after minor changes, + /// such as editing a comment or renaming a label, that can be handled by regenerating + /// selected parts of the DisplayList. + /// + /// + private void RefreshCodeListViewEntries(RangeSet offsetSet) { + IEnumerator iter = offsetSet.RangeListIterator; + while (iter.MoveNext()) { + RangeSet.Range range = iter.Current; + mDisplayList.GenerateRange(range.Low, range.High); + } + } + + private void DoRefreshProject(UndoableChange.ReanalysisScope reanalysisRequired) { + if (reanalysisRequired != UndoableChange.ReanalysisScope.DisplayOnly) { + mGenerationLog = new CommonUtil.DebugLog(); + mGenerationLog.SetMinPriority(CommonUtil.DebugLog.Priority.Debug); + mGenerationLog.SetShowRelTime(true); + + mReanalysisTimer.StartTask("Call DisasmProject.Analyze()"); + mProject.Analyze(reanalysisRequired, mGenerationLog, mReanalysisTimer); + mReanalysisTimer.EndTask("Call DisasmProject.Analyze()"); + } + + if (mGenerationLog != null) { + //mReanalysisTimer.StartTask("Save _log"); + //mGenerationLog.WriteToFile(@"C:\Src\WorkBench\SourceGen\TestData\_log.txt"); + //mReanalysisTimer.EndTask("Save _log"); + + if (mShowAnalyzerOutputDialog != null) { + mShowAnalyzerOutputDialog.BodyText = mGenerationLog.WriteToString(); + } + } + + mReanalysisTimer.StartTask("Generate DisplayList"); + mDisplayList.GenerateAll(); + mReanalysisTimer.EndTask("Generate DisplayList"); + } + + #endregion Project management + + + #region Main window UI event handlers + + /// + /// Invalidates and forces an update on our various windows. + /// + private void InvalidateControls(VirtualListViewSelection newSel) { + codeListView.BeginUpdate(); + ClearCodeListViewCache(); + if (mDisplayList != null) { + codeListView.VirtualListSize = mDisplayList.Count; + } + + mReanalysisTimer.StartTask("Restore selection"); + mRestoringSelection = true; + if (newSel != null) { + RestoreSelection(newSel); // want to do this between Begin/EndUpdate + } else if (mDisplayList != null) { + // No selection to restore. This should only happen for the initial + // render, when nothing is yet selected. + // + // Set the length on the code view. + mCodeViewSelection.SetLength(mDisplayList.Count); + } else { + // Just closed the project, nothing to do at all. + } + + mRestoringSelection = false; + mReanalysisTimer.EndTask("Restore selection"); + + UpdateActionMenu(); + codeListView.EndUpdate(); + + InvalidateSymbolListView(); + InvalidateNotesListView(); + UpdateReferenceView(); + UpdateInfoView(); + } + + /// + /// Updates menu item enable status; necessary because that determines whether the + /// associated keyboard shortcuts are active. + /// + /// Updates the main form title to show project name and modification status. + /// + /// This does not handle items that only toggle enabledness when a project is opened + /// or closed. + /// + private void UpdateMenuItemsAndTitle() { + undoToolStripMenuItem.Enabled = (mProject != null && mProject.CanUndo); + redoToolStripMenuItem.Enabled = (mProject != null && mProject.CanRedo); + + navigateBackToolStripButton.Enabled = (mProject != null && mNavStack.HasBackward); + navigateFwdToolStripButton.Enabled = (mProject != null && mNavStack.HasForward); + + // Update main window title. + StringBuilder sb = new StringBuilder(); + sb.Append(Properties.Resources.TITLE_BASE); + if (mProject != null) { + sb.Append(" - "); + if (string.IsNullOrEmpty(mProjectPathName)) { + sb.Append(Properties.Resources.TITLE_NEW_PROJECT); + } else { + sb.Append(Path.GetFileName(mProjectPathName)); + } + + if (mProject.IsDirty) { + sb.Append(" "); + sb.Append(Properties.Resources.TITLE_MODIFIED); + } + } + Text = sb.ToString(); + } + + /// + /// Restores the ListView selection by applying a diff between the old and + /// new selection bitmaps. + /// + /// The virtual list view doesn't change the selection when we rebuild the + /// list. It would be expensive to set all the bits, so we just update the + /// entries that changed. + /// + /// Before returning, mCodeViewSelection is replaced with curSel. + /// + /// Selection bits for the current display list. + private void RestoreSelection(VirtualListViewSelection curSel) { + Debug.Assert(curSel != null); + + // We have to replace mCodeViewSelection immediately, because changing + // the selection will cause ItemSelectionChanged events to fire, invoking + // callbacks that expect the new selection object. Things will explode if + // the older list was shorter. + VirtualListViewSelection prevSel = mCodeViewSelection; + mCodeViewSelection = curSel; + + // Set everything that has changed between the two sets. + int debugNumChanged = 0; + int count = Math.Min(prevSel.Length, curSel.Length); + int i; + for (i = 0; i < count; i++) { + if (prevSel[i] != curSel[i]) { + codeListView.Items[i].Selected = curSel[i]; + debugNumChanged++; + } + } + // Set everything that wasn't there before. New entries default to unselected, + // so we only need to do this if the new value is "true". + for (; i < curSel.Length; i++) { + // An ItemSelectionChanged event will fire that will cause curSel[i] to + // be assigned. This is fine. + if (curSel[i]) { + codeListView.Items[i].Selected = curSel[i]; + debugNumChanged++; + } + } + + Debug.WriteLine("RestoreSelection: changed " + debugNumChanged + + " of " + curSel.Length + " lines"); + } + + // This gets the key events for all controls associated with the main form, + // regardless of which has focus, making it useful for keyboard shortcuts. + // Return true to indicate that we've handled the key. + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { + // Ctrl-Shift-Z is an alias for Redo (Ctrl-Y). + if (keyData == (Keys.Control | Keys.Shift | Keys.Z)) { + if (redoToolStripMenuItem.Enabled) { + redoToolStripMenuItem_Click(null, null); + } + return true; + } + + // Navigation keys. Alt+left/right is intuitive key binding used by Eclipse, + // Ctrl+[shift]+minus is weird Visual Studio binding. + if (keyData == (Keys.Alt | Keys.Left) || + keyData == (Keys.Control | Keys.OemMinus)) { + if (navigateBackToolStripButton.Enabled) { + navigateBackToolStripButton_Click(null, null); + } + return true; + } + if (keyData == (Keys.Alt | Keys.Right) || + keyData == (Keys.Control | Keys.Shift | Keys.OemMinus)) { + if (navigateFwdToolStripButton.Enabled) { + navigateFwdToolStripButton_Click(null, null); + } + return true; + } + + return base.ProcessCmdKey(ref msg, keyData); + } + + private void navigateBackToolStripButton_Click(object sender, EventArgs e) { + if (!mNavStack.HasBackward) { + // toolbar button should have been disabled + return; + } + int backOff = mNavStack.Pop(); + UpdateMenuItemsAndTitle(); + Debug.WriteLine("Nav back: +" + backOff.ToString("x6")); + GoToOffset(backOff, false, false); + } + + private void navigateFwdToolStripButton_Click(object sender, EventArgs e) { + if (!mNavStack.HasForward) { + // toolbar button should have been disabled + return; + } + int fwdOff = mNavStack.PushPrevious(); + UpdateMenuItemsAndTitle(); + Debug.WriteLine("Nav fwd: +" + fwdOff.ToString("x6")); + GoToOffset(fwdOff, false, false); + } + + /// + /// Determines whether any part of the specified offset is currently visible in the + /// code list view. + /// + private bool IsOffsetVisible(int offset) { + int firstLineIndex = mDisplayList.FindLineIndexByOffset(offset); + int lastLineIndex = firstLineIndex + 1; + while (lastLineIndex < mDisplayList.Count && + mDisplayList[lastLineIndex].FileOffset == offset) { + lastLineIndex++; + } + lastLineIndex--; + //Debug.WriteLine("Check vis: first=" + firstLineIndex + " last=" + lastLineIndex); + return codeListView.IsItemVisible(codeListView.Items[firstLineIndex]) || + codeListView.IsItemVisible(codeListView.Items[lastLineIndex]); + } + + // File > New (Ctrl+N) + private void newToolStripMenuItem_Click(object sender, EventArgs e) { + DoNew(); + } + private void newToolStripButton_Click(object sender, EventArgs e) { + newToolStripMenuItem_Click(sender, e); + } + + private void newFileLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { + DoNew(); + } + + private void DoNew() { + if (!DoClose()) { + return; + } + + Setup.NewProject dlg = new Setup.NewProject(); + dlg.ShowDialog(); + if (dlg.DialogResult == DialogResult.OK) { + bool ok = PrepareNewProject(Path.GetFullPath(dlg.DataFileName), dlg.SystemDef); + if (ok) { + FinishPrep(); + } + } + + dlg.Dispose(); + } + + // File > Open (Ctrl+O) + private void openToolStripMenuItem_Click(object sender, EventArgs e) { + DoOpen(); + } + private void openToolStripButton_Click(object sender, EventArgs e) { + openToolStripMenuItem_Click(sender, e); + } + + private void recentProjectLabel1_LinkClicked(object sender, + LinkLabelLinkClickedEventArgs e) { + Debug.Assert(mRecentProjectPaths.Count > 0); + if (DoClose()) { + DoOpenFile(mRecentProjectPaths[0]); + } + } + + private void recentProjectLabel2_LinkClicked(object sender, + LinkLabelLinkClickedEventArgs e) { + Debug.Assert(mRecentProjectPaths.Count > 1); + if (DoClose()) { + DoOpenFile(mRecentProjectPaths[1]); + } + } + + private void openExistingLabel_LinkClicked(object sender, + LinkLabelLinkClickedEventArgs e) { + DoOpen(); + } + + /// + /// Handles opening an existing project by letting the user select the project file. + /// + private void DoOpen() { + if (!DoClose()) { + return; + } + + OpenFileDialog fileDlg = new OpenFileDialog(); + + fileDlg.Filter = ProjectFile.FILENAME_FILTER + "|" + + Properties.Resources.FILE_FILTER_ALL; + fileDlg.FilterIndex = 1; + if (fileDlg.ShowDialog() != DialogResult.OK) { + return; + } + + string projPathName = Path.GetFullPath(fileDlg.FileName); + DoOpenFile(projPathName); + } + + /// + /// Handles opening an existing project, given a pathname to the project file. + /// + private void DoOpenFile(string projPathName) { + Debug.WriteLine("DoOpenFile: " + projPathName); + Debug.Assert(mProject == null); + + if (!File.Exists(projPathName)) { + string msg = string.Format(Properties.Resources.ERR_FILE_NOT_FOUND, projPathName); + MessageBox.Show(msg, Properties.Resources.ERR_FILE_GENERIC_CAPTION, + MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + DisasmProject newProject = new DisasmProject(); + newProject.UseMainAppDomainForPlugins = mUseMainAppDomainForPlugins; + + // Deserialize the project file. I want to do this before loading the data file + // in case we decide to store the data file name in the project (e.g. the data + // file is a disk image or zip archive, and we need to know which part(s) to + // extract). + if (!ProjectFile.DeserializeFromFile(projPathName, newProject, + out FileLoadReport report)) { + // Should probably use a less-busy dialog for something simple like + // "permission denied", but the open file dialog handles most simple + // stuff directly. + ProjectLoadIssues dlg = new ProjectLoadIssues(); + dlg.Messages = report.Format(); + dlg.CanContinue = false; + dlg.ShowDialog(); + // ignore dlg.DialogResult + dlg.Dispose(); + return; + } + + // Now open the data file, generating the pathname by stripping off the ".dis65" + // extension. If we can't find the file, show a message box and offer the option to + // locate it manually, repeating the process until successful or canceled. + const string UNKNOWN_FILE = "UNKNOWN"; + string dataPathName; + if (projPathName.Length <= ProjectFile.FILENAME_EXT.Length) { + dataPathName = UNKNOWN_FILE; + } else { + dataPathName = projPathName.Substring(0, + projPathName.Length - ProjectFile.FILENAME_EXT.Length); + } + byte[] fileData; + while ((fileData = FindValidDataFile(ref dataPathName, newProject, + out bool cancel)) == null) { + if (cancel) { + // give up + Debug.WriteLine("Abandoning attempt to open project"); + return; + } + } + + // If there were warnings, notify the user and give the a chance to cancel. + if (report.Count != 0) { + ProjectLoadIssues dlg = new ProjectLoadIssues(); + dlg.Messages = report.Format(); + dlg.ShowDialog(); + DialogResult result = dlg.DialogResult; + dlg.Dispose(); + + if (result != DialogResult.OK) { + return; + } + } + + mProject = newProject; + mProjectPathName = mProject.ProjectPathName = projPathName; + mProject.SetFileData(fileData, Path.GetFileName(dataPathName)); + FinishPrep(); + } + + /// + /// Finds and loads the specified data file. The file's length and CRC must match + /// the project's expectations. + /// + /// Full path to file. + /// Project object. + /// Returns true if we want to cancel the attempt. + /// + private byte[] FindValidDataFile(ref string dataPathName, DisasmProject proj, + out bool cancel) { + FileInfo fi = new FileInfo(dataPathName); + if (!fi.Exists) { + Debug.WriteLine("File '" + dataPathName + "' doesn't exist"); + dataPathName = ChooseDataFile(dataPathName, + string.Format(Properties.Resources.OPEN_DATA_DOESNT_EXIST, dataPathName)); + cancel = (dataPathName == null); + return null; + } + if (fi.Length != proj.FileDataLength) { + Debug.WriteLine("File '" + dataPathName + "' has length=" + fi.Length + + ", expected " + proj.FileDataLength); + dataPathName = ChooseDataFile(dataPathName, + string.Format(Properties.Resources.OPEN_DATA_WRONG_LENGTH, + fi.Length, proj.FileDataLength)); + cancel = (dataPathName == null); + return null; + } + byte[] fileData = null; + try { + fileData = LoadDataFile(dataPathName); + } catch (Exception ex) { + Debug.WriteLine("File '" + dataPathName + "' failed to load: " + ex.Message); + dataPathName = ChooseDataFile(dataPathName, + string.Format(Properties.Resources.OPEN_DATA_LOAD_FAILED, ex.Message)); + cancel = (dataPathName == null); + return null; + } + uint crc = CRC32.OnWholeBuffer(0, fileData); + if (crc != proj.FileDataCrc32) { + Debug.WriteLine("File '" + dataPathName + "' has CRC32=" + crc + + ", expected " + proj.FileDataCrc32); + // Format the CRC as signed decimal, so that interested parties can + // easily replace the value in the .dis65 file. + dataPathName = ChooseDataFile(dataPathName, + string.Format(Properties.Resources.OPEN_DATA_WRONG_CRC, + (int) crc, (int) proj.FileDataCrc32)); + cancel = (dataPathName == null); + return null; + } + + cancel = false; + return fileData; + } + + /// + /// Displays a "do you want to pick a different file" message, then (on OK) allows the + /// user to select a file. + /// + /// Pathname of original file. + /// Message to display in the message box. + /// Full path of file to open. + private string ChooseDataFile(string origPath, string errorMsg) { + DataFileLoadIssue dlg = new DataFileLoadIssue(); + dlg.PathName = origPath; + dlg.Message = errorMsg; + dlg.ShowDialog(); + DialogResult result = dlg.DialogResult; + dlg.Dispose(); + if (result != DialogResult.OK) { + return null; + } + + OpenFileDialog fileDlg = new OpenFileDialog(); + fileDlg.FileName = Path.GetFileName(origPath); + fileDlg.Filter = Properties.Resources.FILE_FILTER_ALL; + if (fileDlg.ShowDialog() != DialogResult.OK) { + return null; + } + + string newPath = Path.GetFullPath(fileDlg.FileName); + Debug.WriteLine("User selected data file " + newPath); + return newPath; + } + + // File > Save (Ctrl+S) + private void saveToolStripMenuItem_Click(object sender, EventArgs e) { + if (string.IsNullOrEmpty(mProjectPathName)) { + saveAsToolStripMenuItem_Click(sender, e); + return; + } + + DoSave(mProjectPathName); + } + private void saveToolStripButton_Click(object sender, EventArgs e) { + saveToolStripMenuItem_Click(sender, e); + } + + // File > Save As... + private void saveAsToolStripMenuItem_Click(object sender, EventArgs e) { + SaveFileDialog fileDlg = new SaveFileDialog(); + + fileDlg.Filter = ProjectFile.FILENAME_FILTER + "|" + + Properties.Resources.FILE_FILTER_ALL; + fileDlg.FilterIndex = 1; + fileDlg.ValidateNames = true; + fileDlg.AddExtension = true; + fileDlg.FileName = Path.GetFileName(mDataPathName) + ProjectFile.FILENAME_EXT; + if (fileDlg.ShowDialog() == DialogResult.OK) { + string pathName = Path.GetFullPath(fileDlg.FileName); + Debug.WriteLine("Project save path: " + pathName); + if (DoSave(pathName)) { + // Success, record the path name. + mProjectPathName = mProject.ProjectPathName = pathName; + + // add it to the title bar + UpdateMenuItemsAndTitle(); + } + } + } + + private bool DoSave(string pathName) { + Debug.WriteLine("SAVING " + pathName); + if (!ProjectFile.SerializeToFile(mProject, pathName, out string errorMessage)) { + MessageBox.Show(Properties.Resources.ERR_PROJECT_SAVE_FAIL + ": " + errorMessage, + Properties.Resources.OPERATION_FAILED, + MessageBoxButtons.OK, MessageBoxIcon.Error); + return false; + } + + mProject.ResetDirtyFlag(); + // If the debug dialog is visible, update it. + if (mShowUndoRedoHistoryDialog != null) { + mShowUndoRedoHistoryDialog.BodyText = mProject.DebugGetUndoRedoHistory(); + } + UpdateMenuItemsAndTitle(); + + // Update this, in case this was a new project. + UpdateRecentProjectList(pathName); + + // Seems like a good time to save this off too. + SaveAppSettings(); + + return true; + } + + // App is closing. + private void ProjectView_FormClosing(object sender, FormClosingEventArgs e) { + Debug.WriteLine("Main app form closing (reason=" + e.CloseReason + ")"); + if (!DoClose()) { + e.Cancel = true; + return; + } + SaveAppSettings(); + } + + // File > Close + private void closeToolStripMenuItem_Click(object sender, EventArgs e) { + if (!DoClose()) { + Debug.WriteLine("Close canceled"); + } + } + + /// + /// Closes the project and associated modeless dialogs. Unsaved changes will be + /// lost, so if the project has outstanding changes the user will be given the + /// opportunity to cancel. + /// + /// True if the project was closed, false if the user chose to cancel. + private bool DoClose() { + Debug.WriteLine("ProjectView.DoClose() - dirty=" + + (mProject == null ? "N/A" : mProject.IsDirty.ToString())); + if (mProject != null && mProject.IsDirty) { + DialogResult result = MessageBox.Show(Properties.Resources.UNSAVED_CHANGES, + Properties.Resources.UNSAVED_CHANGES_CAPTION, MessageBoxButtons.OKCancel, + MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2); + if (result == DialogResult.Cancel) { + return false; + } + } + + // Close modeless dialogs that depend on project. + if (mShowUndoRedoHistoryDialog != null) { + mShowUndoRedoHistoryDialog.Close(); + } + if (mShowAnalysisTimersDialog != null) { + mShowAnalysisTimersDialog.Close(); + } + if (mShowAnalyzerOutputDialog != null) { + mShowAnalyzerOutputDialog.Close(); + } + if (mHexDumpDialog != null) { + mHexDumpDialog.Close(); + } + + // Discard all project state. + if (mProject != null) { + mProject.Cleanup(); + mProject = null; + } + mDataPathName = null; + mProjectPathName = null; + mSymbolSubset = new SymbolTableSubset(new SymbolTable()); + mCodeViewSelection = new VirtualListViewSelection(); + mDisplayList = null; + codeListView.VirtualListSize = 0; + codeListView.Items.Clear(); + ShowNoProject(); + InvalidateControls(null); + + mGenerationLog = null; + + // Not necessary, but it lets us check the memory monitor to see if we got + // rid of everything. + GC.Collect(); + + return true; + } + + // File > Assemble... + private void assembleToolStripMenuItem_Click(object sender, EventArgs e) { + if (string.IsNullOrEmpty(mProjectPathName)) { + // We need a project pathname so we know where to write the assembler + // source files, and what to call the output files. We could just pop up the + // Save As dialog, but that seems confusing unless we do a custom dialog with + // an explanation, or have some annoying click-through. + // + // This only appears for never-saved projects, not projects with unsaved data. + MessageBox.Show(Properties.Resources.SAVE_BEFORE_ASM_TEXT, + Properties.Resources.SAVE_BEFORE_ASM_CAPTION, + MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + AsmGen.GenAndAsm dlg = new AsmGen.GenAndAsm(this, mProject, mProjectPathName); + dlg.ShowDialog(); + } + + // File > Exit + private void exitToolStripMenuItem_Click(object sender, EventArgs e) { + // Unsaved-data check happens in form closing event. + Application.Exit(); + } + + // Edit > Undo, Ctrl+Z (may be called with null/null) + private void undoToolStripMenuItem_Click(object sender, EventArgs e) { + if (!mProject.CanUndo) { + Debug.WriteLine("Nothing to undo"); + return; + } + ChangeSet cs = mProject.PopUndoSet(); + ApplyChanges(cs, true); + UpdateMenuItemsAndTitle(); + + // If the debug dialog is visible, update it. + if (mShowUndoRedoHistoryDialog != null) { + mShowUndoRedoHistoryDialog.BodyText = mProject.DebugGetUndoRedoHistory(); + } + } + + // Edit > Redo, Ctrl+Y (may be called with null/null) + private void redoToolStripMenuItem_Click(object sender, EventArgs e) { + if (!mProject.CanRedo) { + Debug.WriteLine("Nothing to redo"); + return; + } + ChangeSet cs = mProject.PopRedoSet(); + ApplyChanges(cs, false); + UpdateMenuItemsAndTitle(); + + // If the debug dialog is visible, update it. + if (mShowUndoRedoHistoryDialog != null) { + mShowUndoRedoHistoryDialog.BodyText = mProject.DebugGetUndoRedoHistory(); + } + } + + // Edit > Select All (Ctrl+A) + private void selectAllToolStripMenuItem_Click(object sender, EventArgs e) { + codeListView.SelectAll(); +#if false + try { + Application.UseWaitCursor = true; + Cursor.Current = Cursors.WaitCursor; + codeListView.BeginUpdate(); + int max = codeListView.VirtualListSize; + for (int i = 0; i < max; i++) { + //codeListView.Items[i].Selected = true; + codeListView.SelectedIndices.Add(i); + if ((i % 50000) == 0) { + toolStripStatusLabel.Text = string.Format( + Properties.Resources.STATUS_SELECTING, (i * 100) / max); + //Application.DoEvents(); // <-- this is unwise + Refresh(); // <-- updates status line but not mouse + } + } + } finally { + codeListView.EndUpdate(); + Application.UseWaitCursor = false; + toolStripStatusLabel.Text = Properties.Resources.STATUS_READY; + } +#endif + } + + // Edit > Copy (Ctrl+C) + private void copyToolStripMenuItem_Click(object sender, EventArgs e) { + const int AssemblerSource = 0; + const int Disassembly = 1; + const bool addCsv = true; + + int format = AppSettings.Global.GetInt(AppSettings.CLIP_LINE_FORMAT, AssemblerSource); + StringBuilder fullText = new StringBuilder(codeListView.SelectedIndices.Count * 50); + StringBuilder csv = new StringBuilder(codeListView.SelectedIndices.Count * 40); + StringBuilder sb = new StringBuilder(100); + + int addrAdj = mProject.CpuDef.HasAddr16 ? 6 : 9; + int disAdj = (format != Disassembly) ? 0 : addrAdj + 10; + + // Walking through the selected indices can be slow for a large file, so we + // run through the full list and pick out the selected items with our parallel + // structure. (I'm assuming that "select all" will be a common precursor.) + for (int i = 0; i < mDisplayList.Count; i++) { + if (!mCodeViewSelection[i]) { + continue; + } + DisplayList.Line line = mDisplayList[i]; + DisplayList.FormattedParts parts = mDisplayList.GetFormattedParts(i); + switch (line.LineType) { + case DisplayList.Line.Type.Code: + case DisplayList.Line.Type.Data: + case DisplayList.Line.Type.EquDirective: + case DisplayList.Line.Type.RegWidthDirective: + case DisplayList.Line.Type.OrgDirective: + if (format == Disassembly) { + if (!string.IsNullOrEmpty(parts.Addr)) { + sb.Append(parts.Addr); + sb.Append(": "); + } + + // shorten the "..." + string bytesStr = parts.Bytes; + if (bytesStr != null && bytesStr.Length > 8) { + bytesStr = bytesStr.Substring(0, 8) + "+"; + } + TextUtil.AppendPaddedString(sb, bytesStr, disAdj); + } + TextUtil.AppendPaddedString(sb, parts.Label, disAdj + 9); + TextUtil.AppendPaddedString(sb, parts.Opcode, disAdj + 9 + 8); + TextUtil.AppendPaddedString(sb, parts.Operand, disAdj + 9 + 8 + 11); + if (string.IsNullOrEmpty(parts.Comment)) { + // Trim trailing spaces off opcode or operand. + TextUtil.TrimEnd(sb); + } else { + sb.Append(parts.Comment); + } + sb.Append("\r\n"); + break; + case DisplayList.Line.Type.LongComment: + if (format == Disassembly) { + TextUtil.AppendPaddedString(sb, string.Empty, disAdj); + } + sb.Append(parts.Comment); + sb.Append("\r\n"); + break; + case DisplayList.Line.Type.Note: + // don't include notes + break; + case DisplayList.Line.Type.Blank: + sb.Append("\r\n"); + break; + default: + Debug.Assert(false); + break; + } + fullText.Append(sb); + + if (addCsv) { + csv.Append(TextUtil.EscapeCSV(parts.Offset)); csv.Append(','); + csv.Append(TextUtil.EscapeCSV(parts.Addr)); csv.Append(','); + csv.Append(TextUtil.EscapeCSV(parts.Bytes)); csv.Append(','); + csv.Append(TextUtil.EscapeCSV(parts.Flags)); csv.Append(','); + csv.Append(TextUtil.EscapeCSV(parts.Attr)); csv.Append(','); + csv.Append(TextUtil.EscapeCSV(parts.Label)); csv.Append(','); + csv.Append(TextUtil.EscapeCSV(parts.Opcode)); csv.Append(','); + csv.Append(TextUtil.EscapeCSV(parts.Operand)); csv.Append(','); + csv.Append(TextUtil.EscapeCSV(parts.Comment)); + csv.Append("\r\n"); + } + + sb.Clear(); + } + + // We want to have both plain text and CSV data on the clipboard. To add both + // formats we need to stream it to a DataObject. Complicating matters is Excel's + // entirely reasonable desire to have data in UTF-8 rather than UTF-16. + // + // (I'm not sure pasting assembly bits into Excel is actually useful, so this + // should probably be optional.) + // + // https://stackoverflow.com/a/369219/294248 + DataObject dataObject = new DataObject(); + dataObject.SetText(fullText.ToString()); + if (addCsv) { + byte[] csvData = Encoding.UTF8.GetBytes(csv.ToString()); + MemoryStream stream = new MemoryStream(csvData); + dataObject.SetData(DataFormats.CommaSeparatedValue, stream); + } + Clipboard.SetDataObject(dataObject, true); + } + + // Edit > Find... (Ctrl+F) + private void findToolStripMenuItem_Click(object sender, EventArgs e) { + FindBox dlg = new FindBox(); + dlg.TextToFind = mFindString; + if (dlg.ShowDialog() == DialogResult.OK) { + mFindString = dlg.TextToFind; + mFindStartIndex = -1; + FindText(); + } + dlg.Dispose(); + } + + // Edit > Find Next (F3) + private void findNextToolStripMenuItem_Click(object sender, EventArgs e) { + FindText(); + } + + private void FindText() { + if (string.IsNullOrEmpty(mFindString)) { + return; + } + + int index; + if (codeListView.SelectedIndices.Count > 0) { + index = codeListView.SelectedIndices[0]; + } else { + index = 0; + } + + // Start one past the currently-selected item. + index++; + if (index == mDisplayList.Count) { + index = 0; + } + //Debug.WriteLine("FindText index=" + index + " start=" + mFindStartIndex + + // " str=" + mFindString); + while (index != mFindStartIndex) { + if (mFindStartIndex < 0) { + // need to latch this inside the loop so the initial test doesn't fail + mFindStartIndex = index; + } + + string searchStr = mDisplayList.GetSearchString(index); + int matchPos = searchStr.IndexOf(mFindString, + StringComparison.InvariantCultureIgnoreCase); + if (matchPos >= 0) { + //Debug.WriteLine("Match " + index + ": " + searchStr); + codeListView.EnsureVisible(index); + codeListView.DeselectAll(); + codeListView.SelectedIndices.Add(index); + return; + } + + index++; + if (index == mDisplayList.Count) { + index = 0; + } + } + + // Announce that we've wrapped around, then clear the start index. + MessageBox.Show(Properties.Resources.FIND_REACHED_START, + Properties.Resources.FIND_REACHED_START_CAPTION, MessageBoxButtons.OK, + MessageBoxIcon.Information); + mFindStartIndex = -1; + } + + // Edit > Go To... + private void gotoToolStripMenuItem_Click(object sender, EventArgs e) { + GotoBox dlg = new GotoBox(mProject, mOutputFormatter); + if (dlg.ShowDialog() == DialogResult.OK) { + GoToOffset(dlg.TargetOffset, false, true); + } + dlg.Dispose(); + } + + // Edit > Project Properties... + private void projectPropertiesToolStripMenuItem_Click(object sender, EventArgs e) { + string projectDir = string.Empty; + if (!string.IsNullOrEmpty(mProjectPathName)) { + projectDir = Path.GetDirectoryName(mProjectPathName); + } + EditProjectProperties dlg = new EditProjectProperties(projectDir); + dlg.SetInitialProps(mProject.ProjectProps); + dlg.NumFormatter = mOutputFormatter; + dlg.ShowDialog(); + ProjectProperties newProps = dlg.NewProps; + dlg.Dispose(); + + if (newProps != null) { + UndoableChange uc = UndoableChange.CreateProjectPropertiesChange( + mProject.ProjectProps, newProps); + ApplyUndoableChanges(new ChangeSet(uc)); + } + } + + // Edit > Settings... + private void settingsToolStripMenuItem_Click(object sender, EventArgs e) { + ShowAppSettings(EditAppSettings.Tab.Unknown); + } + + /// + /// Opens the app settings dialog. + /// + /// Tab to present to the user. + public void ShowAppSettings(EditAppSettings.Tab initialTab) { + EditAppSettings dlg = new EditAppSettings(this, initialTab); + dlg.ShowDialog(); + dlg.Dispose(); + } + + // Help > View Help... + private void viewHelpToolStripMenuItem_Click(object sender, EventArgs e) { + HelpAccess.ShowHelp(HelpAccess.Topic.Contents); + } + private void helpToolStripButton_Click(object sender, EventArgs e) { + viewHelpToolStripMenuItem_Click(sender, e); + } + + // Help > About... + private void aboutToolStripMenuItem_Click(object sender, EventArgs e) { + AboutBox dlg = new AboutBox(); + dlg.ShowDialog(); + dlg.Dispose(); + } + + private void codeListView_MouseClick(object sender, MouseEventArgs e) { + //if (e.Button == MouseButtons.Left) { + // Debug.WriteLine("LEFT CLICK"); + //} else if (e.Button == MouseButtons.Right) { + // Debug.WriteLine("RIGHT CLICK"); + // //ShowRightClickMenu(); + //} else { + // Debug.WriteLine("CLICK " + e.Button); + //} + } + + private void codeListView_MouseDoubleClick(object sender, MouseEventArgs e) { + ListViewHitTestInfo info = codeListView.HitTest(e.X, e.Y); + int row = info.Item.Index; + int col = info.Item.SubItems.IndexOf(info.SubItem); + // col will be -1 for e.g. blank lines [not anymore?] + string value = col < 0 ? "-" : info.Item.SubItems[col].Text; + Debug.WriteLine(string.Format("R{0}:C{1} val '{2}'", row, col, value)); + + // It's possible to select multiple lines with shift-double-click. We + // handle that by checking to see what UpdateActionMenu() decided was available. + + // Clicking on some types of lines, such as ORG directives, results in + // specific behavior regardless of which column you click in. We're just + // checking the clicked-on line to decide what action to take. If it doesn't + // make sense to do for a multi-line selection, the action will have been + // disabled. + DisplayList.Line line = mDisplayList[row]; + switch (line.LineType) { + case DisplayList.Line.Type.EquDirective: + // Currently only does something for project symbols; platform symbols + // do nothing. + if (editProjectSymbolToolStripMenuItem.Enabled) { + EditProjectSymbol_Click(sender, e); + } + break; + case DisplayList.Line.Type.OrgDirective: + if (setAddressToolStripMenuItem.Enabled) { + EditAddress_Click(sender, e); + } + break; + case DisplayList.Line.Type.RegWidthDirective: + if (overrideStatusFlagsToolStripMenuItem.Enabled) { + EditStatusFlags_Click(sender, e); + } + break; + case DisplayList.Line.Type.LongComment: + if (editLongCommentToolStripMenuItem.Enabled) { + EditLongComment_Click(sender, e); + } + break; + case DisplayList.Line.Type.Note: + if (editNoteToolStripMenuItem.Enabled) { + EditNote_Click(sender, e); + } + break; + + case DisplayList.Line.Type.Code: + case DisplayList.Line.Type.Data: + // For code and data, we have to break it down by column. + switch ((ColumnIndex)col) { + case ColumnIndex.Offset: + // does nothing + break; + case ColumnIndex.Address: + // edit address + if (setAddressToolStripMenuItem.Enabled) { + EditAddress_Click(sender, e); + } + break; + case ColumnIndex.Bytes: + if (showHexDumpToolStripMenuItem.Enabled) { + ShowHexDump_Click(sender, e); + } + break; + case ColumnIndex.Flags: + if (overrideStatusFlagsToolStripMenuItem.Enabled) { + EditStatusFlags_Click(sender, e); + } + break; + case ColumnIndex.Attributes: + // does nothing + break; + case ColumnIndex.Label: + if (editLabelToolStripMenuItem.Enabled) { + EditLabel_Click(sender, e); + } + break; + case ColumnIndex.Opcode: + // File offset should always be valid, since we excluded the EQU + // statements and header comment earlier. + if (line.FileOffset >= 0) { + Anattrib attr = mProject.GetAnattrib(line.FileOffset); + // Does this have an operand with an in-file target offset? + if (attr.OperandOffset >= 0) { + // Yup, find the line for that offset and jump to it. + GoToOffset(attr.OperandOffset, false, true); + //int targetIndex = + // mDisplayList.FindLineIndexByOffset(attr.OperandOffset); + //GoToOffset(mDisplayList[targetIndex].FileOffset); + } else if (attr.IsDataStart || attr.IsInlineDataStart) { + // If it's an Address or Symbol, we can try to resolve + // the value. + int operandOffset = DataAnalysis.GetDataOperandOffset( + mProject, line.FileOffset); + if (operandOffset >= 0) { + GoToOffset(operandOffset, false, true); + //int targetIndex = + // mDisplayList.FindLineIndexByOffset(operandOffset); + //GoToOffset(mDisplayList[targetIndex].FileOffset); + } + } + } + break; + case ColumnIndex.Operand: + if (editOperandToolStripMenuItem.Enabled) { + EditOperand_Click(sender, e); + } else if (editDataFormatToolStripMenuItem.Enabled) { + EditData_Click(sender, e); + } + break; + case ColumnIndex.Comment: + if (editCommentToolStripMenuItem.Enabled) { + EditComment_Click(sender, e); + } + break; + + } + break; + + default: + Debug.WriteLine("Double-click: unhandled line type " + line.LineType); + break; + } + } + + /// + /// Moves the view and selection to the specified offset. We want to select stuff + /// differently if we're jumping to a note vs. jumping to an instruction. + /// + /// Offset to jump to. + /// If set, push new offset onto navigation stack. + private void GoToOffset(int gotoOffset, bool jumpToNote, bool doPush) { + int curSelIndex = -1; + if (codeListView.SelectedIndices.Count > 0) { + curSelIndex = codeListView.SelectedIndices[0]; + } + + int topLineIndex = mDisplayList.FindLineIndexByOffset(gotoOffset); + if (topLineIndex < 0) { + Debug.Assert(false, "failed goto offset +" + gotoOffset.ToString("x6")); + return; + } + int lastLineIndex; + if (jumpToNote) { + // Select all note lines, disregard the rest. + while (mDisplayList[topLineIndex].LineType != DisplayList.Line.Type.Note) { + topLineIndex++; + Debug.Assert(mDisplayList[topLineIndex].FileOffset == gotoOffset); + } + lastLineIndex = topLineIndex + 1; + while (lastLineIndex < mDisplayList.Count && + mDisplayList[lastLineIndex].LineType == DisplayList.Line.Type.Note) { + lastLineIndex++; + } + } else if (gotoOffset < 0) { + // This is the offset of the header comment or a .EQ directive. Don't mess with it. + lastLineIndex = topLineIndex + 1; + } else { + // Advance to the code or data line. + while (mDisplayList[topLineIndex].LineType != DisplayList.Line.Type.Code && + mDisplayList[topLineIndex].LineType != DisplayList.Line.Type.Data) { + topLineIndex++; + } + lastLineIndex = topLineIndex + 1; + } + + // Make sure the item is visible. For notes, this can span multiple lines. + codeListView.EnsureVisible(lastLineIndex - 1); + codeListView.EnsureVisible(topLineIndex); + + // Update the selection. + codeListView.DeselectAll(); + for (int i = topLineIndex; i < lastLineIndex; i++) { + codeListView.Items[i].Selected = true; + } + + if (doPush) { + if (curSelIndex >= 0) { + // Update the back stack and associated controls. + mNavStack.Push(mDisplayList[curSelIndex].FileOffset, gotoOffset); + UpdateMenuItemsAndTitle(); + } else { + // This can happen when the project is first opened and nothing is selected. + Debug.WriteLine("no selection to go back to"); + } + } + } + + // Fires when the selection changes, causing the SelectionIndices list to change. + // For multi-select, this seems to be called with an empty list. + private void codeListView_SelectedIndexChanged(object sender, EventArgs e) { + // Update the "references" and "info" window contents. + UpdateReferenceView(); + UpdateInfoView(); + + UpdateSelectionHighlight(); + } + + // Virtual ListView selection tracking + private void codeListView_ItemSelectionChanged(object sender, + ListViewItemSelectionChangedEventArgs e) { + mCodeViewSelection.ItemSelectionChanged(e); + + // Don't try to call mCodeViewSelection.DebugValidateSelectionCount here. + // Events will fire during RestoreSelection() at a point where the + // SelectedIndices don't match up. + + if (!mRestoringSelection) { + UpdateActionMenu(); + } + } + + // Virtual ListView selection tracking + private void codeListView_VirtualItemsSelectionRangeChanged(object sender, + ListViewVirtualItemsSelectionRangeChangedEventArgs e) { + mCodeViewSelection.VirtualItemsSelectionRangeChanged(e); + + if (!mRestoringSelection) { + UpdateActionMenu(); + } + } + + /// + /// Enables or disables the menu items in the Actions menu. + /// + /// We want to do this whenever the selection changes so that any keyboard shortcuts + /// are enabled or disabled appropriately. This does need to be reasonably fast + /// for large files. + /// + /// The outcome of this method -- menu items being enabled or disabled -- is also + /// used by the double-click handler. + /// + private void UpdateActionMenu() { + if (mProject == null) { + // Disable all actions. + foreach (ToolStripItem item in mActionsMenuItems) { + item.Enabled = false; + } + return; + } + + // While restoring the selection, the SelectedIndices won't match up, because + // part of the restore process is setting and clearing the control to match + // what's in the mCodeViewSelection array. There's no value in repeating + // this for every event caused by the restoration, so we don't expect to be + // called at all during a restore. (Just make sure to call here after the + // restore is complete.) + Debug.Assert(!mRestoringSelection); + Debug.Assert(mCodeViewSelection.DebugValidateSelectionCount( + codeListView.SelectedIndices.Count), "selection count mismatch"); + + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + EntityCounts entityCounts; + + // Use IsSingleItemSelected(), rather than just checking sel.Count, because we + // want the user to be able to e.g. EditData on a multi-line string even if all + // lines in the string are selected. + if (IsSingleItemSelected()) { + entityCounts = GatherEntityCounts(sel[0]); + + DisplayList.Line line = mDisplayList[sel[0]]; + DisplayList.Line.Type lineType = line.LineType; + + bool isCodeOrData = (lineType == DisplayList.Line.Type.Code || + lineType == DisplayList.Line.Type.Data); + + setAddressToolStripMenuItem.Enabled = + (isCodeOrData || lineType == DisplayList.Line.Type.OrgDirective); + editOperandToolStripMenuItem.Enabled = + (lineType == DisplayList.Line.Type.Code && + mProject.GetAnattrib(line.FileOffset).IsInstructionWithOperand); + editDataFormatToolStripMenuItem.Enabled = + (lineType == DisplayList.Line.Type.Data); + editLabelToolStripMenuItem.Enabled = isCodeOrData; + editCommentToolStripMenuItem.Enabled = isCodeOrData; + editLongCommentToolStripMenuItem.Enabled = + (isCodeOrData || lineType == DisplayList.Line.Type.LongComment); + editNoteToolStripMenuItem.Enabled = + (isCodeOrData || lineType == DisplayList.Line.Type.Note); + overrideStatusFlagsToolStripMenuItem.Enabled = + (lineType == DisplayList.Line.Type.Code || + lineType == DisplayList.Line.Type.RegWidthDirective); + deleteNoteCommentToolStripMenuItem.Enabled = + (lineType == DisplayList.Line.Type.LongComment || + lineType == DisplayList.Line.Type.Note); + + if (lineType == DisplayList.Line.Type.EquDirective) { + // Only enable this for project symbols, not all EQU directives. + int symIndex = DisplayList.DefSymIndexFromOffset(line.FileOffset); + DefSymbol defSym = mProject.ActiveDefSymbolList[symIndex]; + editProjectSymbolToolStripMenuItem.Enabled = + (defSym.SymbolSource == Symbol.Source.Project); + } else { + editProjectSymbolToolStripMenuItem.Enabled = false; + } + } else { + entityCounts = GatherEntityCounts(-1); + + // Disable all single-target-only items. + setAddressToolStripMenuItem.Enabled = false; + editOperandToolStripMenuItem.Enabled = false; + editLabelToolStripMenuItem.Enabled = false; + editCommentToolStripMenuItem.Enabled = false; + editLongCommentToolStripMenuItem.Enabled = false; + editNoteToolStripMenuItem.Enabled = false; + overrideStatusFlagsToolStripMenuItem.Enabled = false; + deleteNoteCommentToolStripMenuItem.Enabled = false; + editProjectSymbolToolStripMenuItem.Enabled = false; + + if (sel.Count == 0) { + // Disable everything else. + editDataFormatToolStripMenuItem.Enabled = false; + hintAsCodeToolStripMenuItem.Enabled = false; + hintAsDataToolStripMenuItem.Enabled = false; + hintAsInlineDataToolStripMenuItem.Enabled = false; + removeHintToolStripMenuItem.Enabled = false; + } else { + // Must be all data items. Blank lines are okay. Currently allowing + // control lines as well. + editDataFormatToolStripMenuItem.Enabled = + (entityCounts.mDataLines > 0 && entityCounts.mCodeLines == 0); + } + } + + toggleSingleBytesToolStripMenuItem.Enabled = + (entityCounts.mDataLines > 0 && entityCounts.mCodeLines == 0); + + // So long as some code or data is highlighted, allow these. Don't worry about + // control lines. Disable options that would have no effect. + bool enableHints = (entityCounts.mDataLines > 0 || entityCounts.mCodeLines > 0); + hintAsCodeToolStripMenuItem.Enabled = enableHints && + (entityCounts.mDataHints != 0 || + entityCounts.mInlineDataHints != 0 || + entityCounts.mNoHints != 0); + hintAsDataToolStripMenuItem.Enabled = enableHints && + (entityCounts.mCodeHints != 0 || + entityCounts.mInlineDataHints != 0 || + entityCounts.mNoHints != 0); + hintAsInlineDataToolStripMenuItem.Enabled = enableHints && + (entityCounts.mCodeHints != 0 || + entityCounts.mDataHints != 0 || + entityCounts.mNoHints != 0); + removeHintToolStripMenuItem.Enabled = enableHints && + (entityCounts.mCodeHints != 0 || + entityCounts.mDataHints != 0 || + entityCounts.mInlineDataHints != 0); + + // Just leave this on. If they're in EQU-land or nothing is selected, it'll just + // open at the start of the file. + showHexDumpToolStripMenuItem.Enabled = true; + } + + /// + /// Entity count collection, for GatherEntityCounts. + /// + private class EntityCounts { + public int mCodeLines; + public int mDataLines; + public int mBlankLines; + public int mControlLines; + + public int mCodeHints; + public int mDataHints; + public int mInlineDataHints; + public int mNoHints; + }; + + /// + /// Gathers a count of different line types and offset hinting. + /// + /// If a single line is selected, pass the index in. + /// Otherwise, pass -1 to traverse the entire line list. + /// + private EntityCounts GatherEntityCounts(int singleLineIndex) { + //DateTime startWhen = DateTime.Now; + int codeLines, dataLines, blankLines, controlLines; + int codeHints, dataHints, inlineDataHints, noHints; + codeLines = dataLines = blankLines = controlLines = 0; + codeHints = dataHints = inlineDataHints = noHints = 0; + + int startIndex, endIndex; + if (singleLineIndex < 0) { + startIndex = 0; + endIndex = mDisplayList.Count - 1; + } else { + startIndex = endIndex = singleLineIndex; + } + + for (int i = startIndex; i <= endIndex; i++) { + if (!mCodeViewSelection[i]) { + continue; + } + DisplayList.Line line = mDisplayList[i]; + switch (line.LineType) { + case DisplayList.Line.Type.Code: + codeLines++; + break; + case DisplayList.Line.Type.Data: + dataLines++; + break; + case DisplayList.Line.Type.Blank: + // Don't generally care how many blank lines there are, but we do want + // to exclude them from the other categories: if we have nothing but + // blank lines, there's nothing to do. + blankLines++; + break; + default: + // These are only editable as single-line items. We do allow mass + // code hint selection to include them (they will be ignored). + // org, equ, rwid, long comment... + controlLines++; + break; + } + + // A single line can span multiple offsets, each of which could have a + // different hint. + for (int offset = line.FileOffset; offset < line.FileOffset + line.OffsetSpan; + offset++) { + switch (mProject.TypeHints[offset]) { + case CodeAnalysis.TypeHint.Code: + codeHints++; + break; + case CodeAnalysis.TypeHint.Data: + dataHints++; + break; + case CodeAnalysis.TypeHint.InlineData: + inlineDataHints++; + break; + case CodeAnalysis.TypeHint.NoHint: + noHints++; + break; + default: + Debug.Assert(false); + break; + } + } + } + + //Debug.WriteLine("GatherEntityCounts (len=" + mCodeViewSelection.Length + ") took " + + // (DateTime.Now - startWhen).TotalMilliseconds + " ms"); + + return new EntityCounts() { + mCodeLines = codeLines, + mDataLines = dataLines, + mBlankLines = blankLines, + mControlLines = controlLines, + mCodeHints = codeHints, + mDataHints = dataHints, + mInlineDataHints = inlineDataHints, + mNoHints = noHints + }; + } + + /// + /// Determines whether the current selection spans a single item. This could be a + /// single-line item or a multi-line item. + /// + private bool IsSingleItemSelected() { + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + if (sel.Count == 0) { + return false; + } else if (sel.Count == 1) { + return true; + } + + // The selection is presented in sorted order, so we can just check the + // first and last entries to see if they're the same. + // + // Performance note: SelectedIndices[] appears to be dynamic. In a very + // large list (500K+ entries), requesting sel[sel.Count - 1] can take a few + // seconds. As an optimization we just give up if the selection spans + // more than a few hundred lines. (At worst, this requires clicking a single line + // of a comment/note to edit it as an individual item.) + // TODO(maybe): iterate over mCodeViewSelection instead? Would need more stuff there. + if (sel.Count >= 500) { + Debug.WriteLine("Selection very large (" + sel.Count + "), not checking for " + + "single-item span"); + return false; + } + DisplayList.Line firstItem = mDisplayList[sel[0]]; + DisplayList.Line lastItem = mDisplayList[sel[sel.Count - 1]]; + if (firstItem.FileOffset == lastItem.FileOffset && + firstItem.LineType == lastItem.LineType) { + return true; + } + return false; + } + + /// + /// Updates the selection highlight. When a code item with an operand offset is + /// selected, such as a branch, we want to highlight the address and label of the + /// target. + /// + private void UpdateSelectionHighlight() { + int targetIndex = FindSelectionHighlight(); + + if (mTargetHighlightIndex != targetIndex) { + mTargetHighlightIndex = targetIndex; + Debug.WriteLine("Selection highlight now " + targetIndex); + + // Force a redraw. + codeListView.BeginUpdate(); + //ClearCodeListViewCache(); // not necessary; only formatting has changed + codeListView.EndUpdate(); + } + } + + private int FindSelectionHighlight() { + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + if (sel.Count != 1) { + return -1; + } + DisplayList.Line line = mDisplayList[codeListView.SelectedIndices[0]]; + if (!line.IsCodeOrData) { + return -1; + } + Debug.Assert(line.FileOffset >= 0); + + // Does this have an operand with an in-file target offset? + Anattrib attr = mProject.GetAnattrib(line.FileOffset); + if (attr.OperandOffset >= 0) { + return mDisplayList.FindCodeDataIndexByOffset(attr.OperandOffset); + } else if (attr.IsDataStart || attr.IsInlineDataStart) { + // If it's an Address or Symbol, we can try to resolve + // the value. + int operandOffset = DataAnalysis.GetDataOperandOffset(mProject, line.FileOffset); + if (operandOffset >= 0) { + return mDisplayList.FindCodeDataIndexByOffset(operandOffset); + } + } + return -1; + } + + /// + /// Handles an "opening" event for the codeListView's ContextMenuStrip. + /// + /// This puts all of the Actions menu items in the pop-up context menu. + /// + private void codeListView_CmsOpening(object sender, CancelEventArgs e) { + codeListView.ContextMenuStrip.Items.AddRange(mActionsMenuItems); + e.Cancel = false; + } + + /// + /// Handles an "opening" event for the ProjectView's Actions menu. + /// + /// This puts all of the Actions menu items in the Actions menu. + /// + private void ActionsMenuOpening(object sender, EventArgs e) { + actionsToolStripMenuItem.DropDownItems.AddRange(mActionsMenuItems); + } + + private void EditAddress_Click(Object sender, EventArgs e) { + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + Debug.Assert(IsSingleItemSelected()); + int offset = mDisplayList[sel[0]].FileOffset; + + EditAddress dlg = new EditAddress(); + Anattrib attr = mProject.GetAnattrib(offset); + dlg.MaxAddressValue = mProject.CpuDef.MaxAddressValue; + dlg.SetInitialAddress(attr.Address); + dlg.ShowDialog(); + + if (dlg.DialogResult == DialogResult.OK) { + if (offset == 0 && dlg.Address < 0) { + // Not allowed. The AddressMap will just put it back, which confuses + // the undo operation. + Debug.WriteLine("Not allowed to remove address at offset +000000"); + } else if (attr.Address != dlg.Address) { + Debug.WriteLine("Changing addr at offset +" + offset.ToString("x6") + + " to " + dlg.Address); + + AddressMap addrMap = mProject.AddrMap; + // Get the previous address map entry for this exact offset, if one + // exists. This may be different from the value used as the default + // (attr.Address), which is the address assigned to the offset, in + // the case where no previous mapping existed. + int prevAddress = addrMap.Get(offset); + UndoableChange uc = UndoableChange.CreateAddressChange(offset, + prevAddress, dlg.Address); + ChangeSet cs = new ChangeSet(uc); + ApplyUndoableChanges(cs); + } else { + Debug.WriteLine("No change to address"); + } + } + + dlg.Dispose(); + } + + private void EditOperand_Click(Object sender, EventArgs e) { + Debug.Assert(IsSingleItemSelected()); + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + int offset = mDisplayList[sel[0]].FileOffset; + + EditOperand dlg = new EditOperand(offset, mProject, mOutputFormatter); + + // We'd really like to pass in an indication of what the "default" format actually + // resolved to, but we don't always know. If this offset has a FormatDescriptor, + // we might not have auto-generated the label that would have been used otherwise. + + // We're editing the FormatDescriptor from OperandFormats, not Anattribs; + // the latter may have auto-generated stuff. + if (mProject.OperandFormats.TryGetValue(offset, out FormatDescriptor dfd)) { + dlg.FormatDescriptor = dfd; + } + + dlg.ShowDialog(); + if (dlg.DialogResult == DialogResult.OK) { + ChangeSet cs = new ChangeSet(1); + + if (dlg.FormatDescriptor != dfd && dlg.ShortcutAction != + EditOperand.SymbolShortcutAction.CreateLabelInstead) { + // Note EditOperand returns a null descriptor when the user selects Default. + // This is different from how EditData works, since that has to deal with + // multiple regions. + Debug.WriteLine("Changing " + dfd + " to " + dlg.FormatDescriptor); + UndoableChange uc = UndoableChange.CreateOperandFormatChange(offset, + dfd, dlg.FormatDescriptor); + cs.Add(uc); + } else if (dfd != null && dlg.ShortcutAction == + EditOperand.SymbolShortcutAction.CreateLabelInstead) { + Debug.WriteLine("Removing existing label for CreateLabelInstead"); + UndoableChange uc = UndoableChange.CreateOperandFormatChange(offset, + dfd, null); + cs.Add(uc); + } else { + Debug.WriteLine("No change to format descriptor"); + } + + switch (dlg.ShortcutAction) { + case EditOperand.SymbolShortcutAction.CreateLabelInstead: + case EditOperand.SymbolShortcutAction.CreateLabelAlso: + Debug.Assert(!mProject.UserLabels.ContainsKey(dlg.ShortcutArg)); + Anattrib targetAttr = mProject.GetAnattrib(dlg.ShortcutArg); + Symbol newLabel = new Symbol(dlg.FormatDescriptor.SymbolRef.Label, + targetAttr.Address, Symbol.Source.User, Symbol.Type.LocalOrGlobalAddr); + UndoableChange uc = UndoableChange.CreateLabelChange(dlg.ShortcutArg, + null, newLabel); + cs.Add(uc); + break; + case EditOperand.SymbolShortcutAction.CreateProjectSymbolAlso: + Debug.Assert(!mProject.ProjectProps.ProjectSyms.ContainsKey( + dlg.FormatDescriptor.SymbolRef.Label)); + DefSymbol defSym = new DefSymbol(dlg.FormatDescriptor.SymbolRef.Label, + dlg.ShortcutArg, Symbol.Source.Project, Symbol.Type.ExternalAddr, + FormatDescriptor.SubType.Hex, string.Empty, string.Empty); + ProjectProperties newProps = new ProjectProperties(mProject.ProjectProps); + newProps.ProjectSyms.Add(defSym.Label, defSym); + uc = UndoableChange.CreateProjectPropertiesChange( + mProject.ProjectProps, newProps); + cs.Add(uc); + break; + case EditOperand.SymbolShortcutAction.None: + break; + } + + if (cs.Count != 0) { + ApplyUndoableChanges(cs); + } + } + + dlg.Dispose(); + } + + private void EditData_Click(Object sender, EventArgs e) { + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + Debug.Assert(sel.Count > 0); + + EditData dlg = new EditData(mProject.FileData, mProject.SymbolTable, mOutputFormatter); + + TypedRangeSet trs = dlg.Selection = GroupedOffsetSetFromSelected(); + if (trs.Count == 0) { + Debug.Assert(false, "EditData found nothing to edit"); // shouldn't happen + dlg.Dispose(); + return; + } + + // If the first offset has a FormatDescriptor, pass that in as a recommendation + // for the default value in the dialog. This allows single-item editing to work + // as expected. If the format can't be applied to the full selection (which + // would disable that radio button), the dialog will have to pick something + // that does work. + // + // We could pull this out of Anattribs, which would let the dialog reflect the + // auto-format that the user was just looking at. However, I think it's better + // if the dialog shows what's actually there, i.e. no formatting at all. + IEnumerator iter = + (IEnumerator) trs.GetEnumerator(); + iter.MoveNext(); + TypedRangeSet.Tuple firstOffset = iter.Current; + if (mProject.OperandFormats.TryGetValue(firstOffset.Value, out FormatDescriptor dfd)) { + dlg.FirstFormatDescriptor = dfd; + } + + dlg.ShowDialog(); + if (dlg.DialogResult == DialogResult.OK) { + // Merge the changes into the OperandFormats list. We need to remove all + // FormatDescriptors that overlap the selected region. We don't need to + // pass the selection set in, because the dlg.Results list spans the exact + // set of ranges. + // + // If nothing actually changed, don't generate an undo record. + ChangeSet cs = mProject.GenerateFormatMergeSet(dlg.Results); + if (cs.Count != 0) { + ApplyUndoableChanges(cs); + } else { + Debug.WriteLine("No change to data formats"); + } + } + + dlg.Dispose(); + } + + private void ToggleSingleBytes_Click(object sender, EventArgs e) { + TypedRangeSet trs = GroupedOffsetSetFromSelected(); + if (trs.Count == 0) { + Debug.Assert(false, "nothing to edit"); // shouldn't happen + return; + } + + // Check the format descriptor of the first selected offset. + int firstOffset = -1; + foreach (TypedRangeSet.Tuple tup in trs) { + firstOffset = tup.Value; + break; + } + Debug.Assert(mProject.GetAnattrib(firstOffset).IsDataStart); + bool toDefault = false; + if (mProject.OperandFormats.TryGetValue(firstOffset, out FormatDescriptor curDfd)) { + if (curDfd.FormatType == FormatDescriptor.Type.NumericLE && + curDfd.FormatSubType == FormatDescriptor.SubType.None && + curDfd.Length == 1) { + // Currently single-byte, toggle to default. + toDefault = true; + } + } + + // Iterate through the selected regions. + SortedList newFmts = new SortedList(); + IEnumerator rngIter = trs.RangeListIterator; + while (rngIter.MoveNext()) { + TypedRangeSet.TypedRange rng = rngIter.Current; + if (toDefault) { + // Create a single REMOVE descriptor that covers the full span. + FormatDescriptor newDfd = FormatDescriptor.Create(rng.High - rng.Low + 1, + FormatDescriptor.Type.REMOVE, FormatDescriptor.SubType.None); + newFmts.Add(rng.Low, newDfd); + } else { + // Add individual single-byte format descriptors for everything. + FormatDescriptor newDfd = FormatDescriptor.Create(1, + FormatDescriptor.Type.NumericLE, FormatDescriptor.SubType.None); + for (int i = rng.Low; i <= rng.High; i++) { + newFmts.Add(i, newDfd); + } + } + } + + ChangeSet cs = mProject.GenerateFormatMergeSet(newFmts); + if (cs.Count != 0) { + ApplyUndoableChanges(cs); + } + } + + private void DeleteNoteComment_Click(object sender, EventArgs e) { + Debug.Assert(IsSingleItemSelected()); + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + DisplayList.Line line = mDisplayList[sel[0]]; + int offset = line.FileOffset; + + UndoableChange uc; + if (line.LineType == DisplayList.Line.Type.Note) { + if (!mProject.Notes.TryGetValue(offset, out MultiLineComment oldNote)) { + Debug.Assert(false); + return; + } + uc = UndoableChange.CreateNoteChange(offset, oldNote, null); + } else if (line.LineType == DisplayList.Line.Type.LongComment) { + if (!mProject.LongComments.TryGetValue(offset, out MultiLineComment oldComment)) { + Debug.Assert(false); + return; + } + uc = UndoableChange.CreateLongCommentChange(offset, oldComment, null); + } else { + Debug.Assert(false); + return; + } + ChangeSet cs = new ChangeSet(uc); + ApplyUndoableChanges(cs); + } + + private void EditLabel_Click(Object sender, EventArgs e) { + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + Debug.Assert(IsSingleItemSelected()); + int offset = mDisplayList[sel[0]].FileOffset; + + EditLabel dlg = new EditLabel(mProject.SymbolTable); + Anattrib attr = mProject.GetAnattrib(offset); + + dlg.LabelSym = attr.Symbol; + dlg.Address = attr.Address; + dlg.ShowDialog(); + if (dlg.DialogResult == DialogResult.OK) { + // NOTE: if label matching is case-insensitive, we want to allow a situation + // where a label is being renamed from "FOO" to "Foo". (We should be able to + // test for object equality on the Symbol.) + if (attr.Symbol != dlg.LabelSym) { + Debug.WriteLine("Changing label at offset +" + offset.ToString("x6")); + + // For undo/redo, we want to update the UserLabels value. This may + // be different from the Anattrib symbol, which can have an auto-generated + // value. + Symbol oldUserValue = null; + if (mProject.UserLabels.ContainsKey(offset)) { + oldUserValue = mProject.UserLabels[offset]; + } + UndoableChange uc = UndoableChange.CreateLabelChange(offset, + oldUserValue, dlg.LabelSym); + ChangeSet cs = new ChangeSet(uc); + ApplyUndoableChanges(cs); + } + } + + dlg.Dispose(); + } + + private void EditStatusFlags_Click(Object sender, EventArgs e) { + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + Debug.Assert(IsSingleItemSelected()); + int offset = mDisplayList[sel[0]].FileOffset; + + EditStatusFlags dlg = new EditStatusFlags(); + dlg.HasEmuFlag = mProject.CpuDef.HasEmuFlag; + dlg.FlagValue = mProject.StatusFlagOverrides[offset]; + dlg.ShowDialog(); + if (dlg.DialogResult == DialogResult.OK) { + if (dlg.FlagValue != mProject.StatusFlagOverrides[offset]) { + UndoableChange uc = UndoableChange.CreateStatusFlagChange(offset, + mProject.StatusFlagOverrides[offset], dlg.FlagValue); + ChangeSet cs = new ChangeSet(uc); + ApplyUndoableChanges(cs); + } + } + + dlg.Dispose(); + } + + private void EditComment_Click(Object sender, EventArgs e) { + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + Debug.Assert(IsSingleItemSelected()); + int offset = mDisplayList[sel[0]].FileOffset; + + EditComment dlg = new EditComment(); + string oldComment = dlg.Comment = mProject.Comments[offset]; + dlg.ShowDialog(); + + if (dlg.DialogResult == DialogResult.OK) { + if (!oldComment.Equals(dlg.Comment)) { + Debug.WriteLine("Changing comment at +" + offset.ToString("x6")); + + UndoableChange uc = UndoableChange.CreateCommentChange(offset, + oldComment, dlg.Comment); + ChangeSet cs = new ChangeSet(uc); + ApplyUndoableChanges(cs); + } + } + + dlg.Dispose(); + } + + private void EditLongComment_Click(Object sender, EventArgs e) { + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + Debug.Assert(IsSingleItemSelected()); + EditLongComment(mDisplayList[sel[0]].FileOffset); + } + + private void editHeaderCommentToolStripMenuItem_Click(object sender, EventArgs e) { + EditLongComment(DisplayList.Line.HEADER_COMMENT_OFFSET); + } + + private void EditLongComment(int offset) { + EditLongComment dlg = new EditLongComment(mOutputFormatter); + if (mProject.LongComments.TryGetValue(offset, out MultiLineComment oldComment)) { + dlg.LongComment = oldComment; + } + dlg.ShowDialog(); + + if (dlg.DialogResult == DialogResult.OK) { + MultiLineComment newComment = dlg.LongComment; + if (oldComment != newComment) { + Debug.WriteLine("Changing long comment at +" + offset.ToString("x6")); + + UndoableChange uc = UndoableChange.CreateLongCommentChange(offset, + oldComment, newComment); + ChangeSet cs = new ChangeSet(uc); + ApplyUndoableChanges(cs); + } + } + + dlg.Dispose(); + } + + private void EditNote_Click(Object sender, EventArgs e) { + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + Debug.Assert(IsSingleItemSelected()); + int offset = mDisplayList[sel[0]].FileOffset; + + EditNote dlg = new EditNote(); + if (mProject.Notes.TryGetValue(offset, out MultiLineComment oldNote)) { + dlg.Note = oldNote; + } + dlg.ShowDialog(); + + if (dlg.DialogResult == DialogResult.OK) { + MultiLineComment newNote = dlg.Note; + if (oldNote != newNote) { + Debug.WriteLine("Changing note at +" + offset.ToString("x6")); + + UndoableChange uc = UndoableChange.CreateNoteChange(offset, + oldNote, newNote); + ChangeSet cs = new ChangeSet(uc); + ApplyUndoableChanges(cs); + } + } + + dlg.Dispose(); + } + + private void EditProjectSymbol_Click(object sender, EventArgs e) { + Debug.Assert(IsSingleItemSelected()); + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + DisplayList.Line line = mDisplayList[sel[0]]; + int symIndex = DisplayList.DefSymIndexFromOffset(line.FileOffset); + DefSymbol origDefSym = mProject.ActiveDefSymbolList[symIndex]; + Debug.Assert(origDefSym.SymbolSource == Symbol.Source.Project); + + EditDefSymbol dlg = new EditDefSymbol(mOutputFormatter, + mProject.ProjectProps.ProjectSyms); + dlg.DefSym = origDefSym; + if (dlg.ShowDialog() == DialogResult.OK) { + ProjectProperties newProps = new ProjectProperties(mProject.ProjectProps); + newProps.ProjectSyms.Remove(origDefSym.Label); + newProps.ProjectSyms[dlg.DefSym.Label] = dlg.DefSym; + + UndoableChange uc = UndoableChange.CreateProjectPropertiesChange( + mProject.ProjectProps, newProps); + ChangeSet cs = new ChangeSet(uc); + ApplyUndoableChanges(cs); + } + dlg.Dispose(); + } + + private void MarkAsCode_Click(Object sender, EventArgs e) { + MarkAsType(CodeAnalysis.TypeHint.Code); + } + private void MarkAsData_Click(Object sender, EventArgs e) { + MarkAsType(CodeAnalysis.TypeHint.Data); + } + private void MarkAsInlineData_Click(Object sender, EventArgs e) { + MarkAsType(CodeAnalysis.TypeHint.InlineData); + } + private void MarkAsNoHint_Click(Object sender, EventArgs e) { + MarkAsType(CodeAnalysis.TypeHint.NoHint); + } + private void MarkAsType(CodeAnalysis.TypeHint hint) { + RangeSet sel = OffsetSetFromSelected(); + TypedRangeSet newSet = new TypedRangeSet(); + TypedRangeSet undoSet = new TypedRangeSet(); + + foreach (int offset in sel) { + if (offset < 0) { + // header comment + continue; + } + CodeAnalysis.TypeHint oldType = mProject.TypeHints[offset]; + if (oldType == hint) { + // no change, don't add to set + continue; + } + undoSet.Add(offset, (int)oldType); + newSet.Add(offset, (int)hint); + } + if (newSet.Count == 0) { + Debug.WriteLine("No changes found (" + hint + ", " + sel.Count + " offsets)"); + return; + } + + UndoableChange uc = UndoableChange.CreateTypeHintChange(undoSet, newSet); + ChangeSet cs = new ChangeSet(uc); + + ApplyUndoableChanges(cs); + } + + private void ShowHexDump_Click(object sender, EventArgs e) { + if (mHexDumpDialog == null) { + // Create and show modeless dialog. This one is "always on top" by default, + // to allow the user to click around to various points. + mHexDumpDialog = new Tools.HexDumpViewer(mProject.FileData, mOutputFormatter); + mHexDumpDialog.OnWindowClosing += (arg) => { + Debug.WriteLine("Hex dump dialog closed"); + //showHexDumpToolStripMenuItem.Checked = false; + mHexDumpDialog = null; + }; + mHexDumpDialog.TopMost = true; + mHexDumpDialog.Show(); + //showHexDumpToolStripMenuItem.Checked = true; + } + + // Bring it to the front of the window stack. This also transfers focus to the + // window. + mHexDumpDialog.BringToFront(); + + // Set the dialog's position. + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + if (sel.Count > 0) { + int firstIndex = sel[0]; + int lastIndex = sel[sel.Count - 1]; + // offsets can be < 0 if they've selected EQU statements + int firstOffset = Math.Max(0, mDisplayList[firstIndex].FileOffset); + int lastOffset = Math.Max(firstOffset, mDisplayList[lastIndex].FileOffset + + mDisplayList[lastIndex].OffsetSpan - 1); + mHexDumpDialog.ShowOffsetRange(firstOffset, lastOffset); + } + } + + private void aSCIIChartToolStripMenuItem_Click(object sender, EventArgs e) { + // Show or hide the modeless dialog. + if (mAsciiChartDialog == null) { + Tools.AsciiChart dlg = new Tools.AsciiChart(); + dlg.OnWindowClosing += (arg) => { + Debug.WriteLine("ASCII chart closed"); + aSCIIChartToolStripMenuItem.Checked = false; + mAsciiChartDialog = null; + }; + dlg.Show(); + mAsciiChartDialog = dlg; + aSCIIChartToolStripMenuItem.Checked = true; + } else { + // Ask the dialog to close. Do the cleanup in the event. + mAsciiChartDialog.Close(); + } + + } + + /// + /// Converts the ListView's selected items into a set of offsets. If a line + /// spans multiple offsets (e.g. a 3-byte instruction), offsets for every + /// byte are included. + /// + /// Boundaries such as labels and address changes are ignored. + /// + /// RangeSet with all offsets. + private RangeSet OffsetSetFromSelected() { + RangeSet rs = new RangeSet(); + + foreach (int index in codeListView.SelectedIndices) { + int offset = mDisplayList[index].FileOffset; + + // Mark every byte of an instruction or multi-byte data item -- + // everything that is represented by the line the user selected. + int len; + if (offset >= 0) { + len = mProject.GetAnattrib(offset).Length; + } else { + // header area + len = 1; + } + Debug.Assert(len > 0); + for (int i = offset; i < offset + len; i++) { + rs.Add(i); + } + } + return rs; + } + + /// + /// Converts the ListView's selected items into a set of offsets. If a line + /// spans multiple offsets (e.g. a 3-byte instruction), offsets for every + /// byte are included. + /// + /// Contiguous regions with user labels or address changes are split into + /// independent regions by using a serial number for the range type. Same for + /// long comments and notes. + /// + /// We don't split based on existing data format items. That would make it impossible + /// to convert from (say) a collection of single bytes to a collection of double bytes + /// or a string. It should not be possible to select part of a formatted section, + /// unless the user has been playing weird games with type hints to get overlapping + /// format descriptors. + /// + /// TypedRangeSet with all offsets. + private TypedRangeSet GroupedOffsetSetFromSelected() { + TypedRangeSet rs = new TypedRangeSet(); + int groupNum = 0; + int expectedAddr = -1; + + bool thing = false; + if (thing) { + DateTime nowWhen = DateTime.Now; + int selCount = 0; + for (int i = 0; i < mDisplayList.Count; i++) { + ListViewItem lvi = codeListView.Items[i]; + selCount += lvi.Selected ? 1 : 0; + } + Debug.WriteLine("Sel count (" + selCount + ") took " + + (DateTime.Now - nowWhen).TotalMilliseconds + " ms"); + } + + DateTime startWhen = DateTime.Now; + int prevOffset = -1; + foreach (int index in codeListView.SelectedIndices) { + // Don't add an offset to the set if the only part of it that is selected + // is a directive or blank line. We only care about file offsets, so skip + // anything that isn't code or data. + if (!mDisplayList[index].IsCodeOrData) { + continue; + } + + int offset = mDisplayList[index].FileOffset; + if (offset == prevOffset) { + // This is a continuation of a multi-line item like a string. We've + // already accounted for all bytes associated with this offset. + continue; + } + Anattrib attr = mProject.GetAnattrib(offset); + + if (expectedAddr == -1) { + expectedAddr = attr.Address; + } + // Check for user labels. + if (mProject.UserLabels.ContainsKey(offset)) { + //if (mProject.GetAnattrib(offset).Symbol != null) { + // We consider auto labels when splitting regions for the data analysis, + // but I don't think we want to take them into account here. The specific + // example that threw me was loading a 16-bit value from an address table. + // The code does "LDA table,X / STA / LDA table+1,X / STA", which puts auto + // labels at the first two addresses -- splitting the region. That's good + // for the uncategorized data analyzer, but very annoying if you want to + // slap a 16-bit numeric format on all entries. + groupNum++; + } else if (mProject.HasCommentOrNote(offset)) { + // Don't carry across a long comment or note. + groupNum++; + } else if (attr.Address != expectedAddr) { + // For a contiguous selection, this should only happen if there's a .ORG + // address change. For a selection that skips code/data lines this is + // expected. In the later case, incrementing the group number is + // unnecessary but harmless. + Debug.WriteLine("Address break: " + attr.Address + " vs. " + expectedAddr); + //Debug.Assert(mProject.AddrMap.Get(offset) >= 0); + + expectedAddr = attr.Address; + groupNum++; + } + + // Mark every byte of an instruction or multi-byte data item -- + // everything that is represented by the line the user selected. Control + // statements and blank lines aren't relevant here, as we only care about + // file offsets. + int len = mDisplayList[index].OffsetSpan; // attr.Length; + Debug.Assert(len > 0); + for (int i = offset; i < offset + len; i++) { + rs.Add(i, groupNum); + } + // Advance the address. + expectedAddr += len; + + prevOffset = offset; + } + Debug.WriteLine("Offset selection conv took " + + (DateTime.Now - startWhen).TotalMilliseconds + " ms"); + return rs; + } + + #endregion // Main window UI event handlers + + + #region codeListView OwnerDraw implementation + + private enum ColumnIndex { + Offset = 0, Address, Bytes, Flags, Attributes, Label, Opcode, Operand, Comment + } + + /// + /// Handy class for collecting column widths for the code list view. + /// + public class CodeListColumnWidths { + public const int NUM_COLUMNS = (int)ColumnIndex.Comment + 1; + + /// + /// Primary storage for column widths. + /// + public int[] Width { get; private set; } + + public int Offset { + get { return Width[0]; } + set { Width[0] = value; } + } + public int Address { + get { return Width[1]; } + set { Width[1] = value; } + } + public int Bytes { + get { return Width[2]; } + set { Width[2] = value; } + } + public int Flags { + get { return Width[3]; } + set { Width[3] = value; } + } + public int Attributes { + get { return Width[4]; } + set { Width[4] = value; } + } + public int Label { + get { return Width[5]; } + set { Width[5] = value; } + } + public int Opcode { + get { return Width[6]; } + set { Width[6] = value; } + } + public int Operand { + get { return Width[7]; } + set { Width[7] = value; } + } + public int Comment { + get { return Width[8]; } + set { Width[8] = value; } + } + + public CodeListColumnWidths() { + Width = new int[NUM_COLUMNS]; + } + + public string Serialize() { + StringBuilder sb = new StringBuilder(64); + sb.Append("cw"); + for (int i = 0; i < NUM_COLUMNS; i++) { + sb.Append(','); + sb.Append(Width[i]); + } + return sb.ToString(); + } + + public static CodeListColumnWidths Deserialize(string cereal) { + CodeListColumnWidths widths = new CodeListColumnWidths(); + string[] splitted = cereal.Split(','); + if (splitted.Length != NUM_COLUMNS + 1) { + Debug.WriteLine("Column width parse failed: wrong count: " + splitted.Length); + return null; + } + if (splitted[0] != "cw") { + Debug.WriteLine("Column width parse failed: bad magic: " + splitted[0]); + return null; + } + try { + for (int i = 0; i < NUM_COLUMNS; i++) { + widths.Width[i] = int.Parse(splitted[i + 1]); + } + } catch (Exception ex) { + Debug.WriteLine("Column width parse failed: " + ex.Message); + return null; + } + return widths; + } + + public override string ToString() { + return Serialize(); + } + } + + /// + /// Gets the default column widths for the code list view, based on the currently + /// configured font. + /// + /// Column width set. + public CodeListColumnWidths GetDefaultCodeListColumnWidths() { + CodeListColumnWidths widths = new CodeListColumnWidths(); + Graphics gfx = codeListView.CreateGraphics(); + widths.Offset = GetCodeListStringWidth(gfx, "X+000000"); + widths.Address = GetCodeListStringWidth(gfx, "X00/0000"); + widths.Bytes = GetCodeListStringWidth(gfx, "X00000000"); + widths.Flags = GetCodeListStringWidth(gfx, "X00000000 0"); + widths.Attributes = GetCodeListStringWidth(gfx, "X######"); + widths.Label = GetCodeListStringWidth(gfx, "XMMMMMMMMM"); + widths.Opcode = GetCodeListStringWidth(gfx, "XMMMMMMM"); + widths.Operand = GetCodeListStringWidth(gfx, "XMMMMMMMMMMMMM"); + widths.Comment = GetCodeListStringWidth(gfx, + "XMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"); + return widths; + } + private int GetCodeListStringWidth(Graphics gfx, string str) { + SizeF size = gfx.MeasureString(str, codeListView.Font); + return (int)Math.Round(size.Width); + } + + /// + /// Saves the code list column widths into AppSettings. + /// + /// + public void SaveCodeListColumnWidths() { + CodeListColumnWidths widths = new CodeListColumnWidths(); + for (int i = 0; i < CodeListColumnWidths.NUM_COLUMNS; i++) { + widths.Width[i] = codeListView.Columns[i].Width; + } + + string cereal = widths.Serialize(); + AppSettings.Global.SetString(AppSettings.CDLV_COL_WIDTHS, cereal); + } + + /// + /// Configures the column widths. + /// + private void SetCodeListHeaderWidths(CodeListColumnWidths widths) { + Debug.WriteLine("Setting column widths: " + widths); + for (int i = 0; i < CodeListColumnWidths.NUM_COLUMNS; i++) { + codeListView.Columns[i].Width = widths.Width[i]; + } + } + + private void codeListView_ColumnWidthChanged(object sender, + ColumnWidthChangedEventArgs e) { + //Debug.WriteLine("Column width changed: " + e.ColumnIndex); + // This fires during initial setup when things don't have widths. A little + // risky to save the widths off now; would be safer to do it right before we write + // the settings file. + SaveCodeListColumnWidths(); + } + + private void codeListView_DrawColumnHeader(object sender, + DrawListViewColumnHeaderEventArgs e) { + ListView lv = e.Header.ListView; + string text = lv.Columns[e.ColumnIndex].Text; + + // Adjust rect to match standard control for 10pt fonts, and + // reserve a couple pixels at the far right end for the separator. + Rectangle rect = new Rectangle(e.Bounds.X + 3, e.Bounds.Y + 4, + e.Bounds.Width - 4, e.Bounds.Height - 4); + TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.EndEllipsis | + TextFormatFlags.SingleLine; + + TextRenderer.DrawText(e.Graphics, text, lv.Font, rect, lv.ForeColor, flags); + + Pen pen = new Pen(Color.LightGray); + //Pen pen = new Pen(Color.Blue); + e.Graphics.DrawLine(pen, e.Bounds.X + e.Bounds.Width - 1, e.Bounds.Y, + e.Bounds.X + e.Bounds.Width - 1, e.Bounds.Y + e.Bounds.Height); + } + + private void codeListView_DrawItem(object sender, + DrawListViewItemEventArgs e) { + // Only draw the full-line items here. Do not draw them later. + + DisplayList.Line line = mDisplayList[e.ItemIndex]; + if (line.LineType != DisplayList.Line.Type.LongComment && + line.LineType != DisplayList.Line.Type.Note) { + return; + } + + // Column 5 is the label. We put long comments and notes there. + int leftColsWidth = 0; + for (int i = 0; i < (int)ColumnIndex.Label; i++) { + leftColsWidth += e.Item.ListView.Columns[i].Width; + } + + // No sub-items, just one long comment. + ListView lv = e.Item.ListView; + ListViewItem lvi = e.Item; + + // Set colors based on selection and focus. + if (lvi.Selected && lv.Focused) { + lvi.BackColor = SystemColors.Highlight; + lvi.ForeColor = lv.BackColor; + } else if (e.Item.Selected && !lv.Focused) { + lvi.BackColor = SystemColors.Control; + lvi.ForeColor = lv.ForeColor; + } else { + lvi.ForeColor = lv.ForeColor; + if (line.BackgroundColor.ToArgb() == 0) { + lvi.BackColor = lv.BackColor; + } else { + // Highlight the entire line. + lvi.BackColor = line.BackgroundColor; + } + } + + e.DrawBackground(); + + if ((e.State & ListViewItemStates.Selected) != 0) { + e.DrawFocusRectangle(); + } + + Rectangle rect = new Rectangle(e.Bounds.X + 3 + leftColsWidth, e.Bounds.Y + 2, + e.Bounds.Width - 3 - leftColsWidth, e.Bounds.Height - 2); + + TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.EndEllipsis | + TextFormatFlags.SingleLine; + + Font font = lv.Font; + TextRenderer.DrawText(e.Graphics, lvi.Text, font, rect, + lvi.ForeColor, flags); + } + + private void codeListView_DrawSubItem(object sender, + DrawListViewSubItemEventArgs e) { + // Draw the multi-column items here. + + ListView lv = e.Item.ListView; + ListViewItem lvi = e.Item; + + DisplayList.Line.Type lineType = mDisplayList[e.ItemIndex].LineType; + if (lineType == DisplayList.Line.Type.LongComment || + lineType == DisplayList.Line.Type.Note) { + return; + } + + // Set colors based on selection and focus. + if (lvi.Selected && lv.Focused) { + e.SubItem.BackColor = SystemColors.Highlight; + e.SubItem.ForeColor = lv.BackColor; + } else if (lvi.Selected && !lv.Focused) { + e.SubItem.BackColor = SystemColors.Control; + e.SubItem.ForeColor = lv.ForeColor; + } else { + if (e.ItemIndex == mTargetHighlightIndex && + (e.ColumnIndex == (int)ColumnIndex.Address || + e.ColumnIndex == (int)ColumnIndex.Label) && + !string.IsNullOrEmpty(e.SubItem.Text)) { + e.SubItem.BackColor = Color.LightBlue; + } else { + e.SubItem.BackColor = lv.BackColor; + } + e.SubItem.ForeColor = lv.ForeColor; + } + + e.DrawBackground(); + + // Shift the text so it lines up with the standard control at 10pts. + // Not strictly necessary, and possibly unwise, since the behavior seems + // to change for larger fonts. + Rectangle rect = new Rectangle(e.Bounds.X + 3, e.Bounds.Y + 2, + e.Bounds.Width - 3, e.Bounds.Height - 2); + + TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.EndEllipsis | + TextFormatFlags.SingleLine; + + Font font = lv.Font; + + TextRenderer.DrawText(e.Graphics, e.SubItem.Text, font, rect, + e.SubItem.ForeColor, flags); + + // Draw the focus rectangle. It's annoying that we have to draw it for every + // sub-item even with FullRowSelect, but DrawItem always happens first, and + // there's no equivalent at the end. (It's unclear how useful the focus rect + // actually is, but it's part of the standard dialog behavior.) + if (lv.FullRowSelect) { + e.DrawFocusRectangle(e.Item.Bounds); + } else { + e.DrawFocusRectangle(e.Bounds); + } + } + #endregion // codeListView OwnerDraw implementation + + + #region codeListView Virtual + // For a half-megabyte file, the ListViewItem creation could take 40+ seconds. + + // Internal array for holding temporary state. Avoids frequent allocations. + private ListViewItem.ListViewSubItem[] mSubArray = + new ListViewItem.ListViewSubItem[CodeListColumnWidths.NUM_COLUMNS - 1]; + + // Array of blank sub-items, for entries that span multiple columns. The + // virtual mode requires fully populating sub-items. + private static ListViewItem.ListViewSubItem[] mBlankArray = + new ListViewItem.ListViewSubItem[CodeListColumnWidths.NUM_COLUMNS - 1] { + new ListViewItem.ListViewSubItem(), + new ListViewItem.ListViewSubItem(), + new ListViewItem.ListViewSubItem(), + new ListViewItem.ListViewSubItem(), + new ListViewItem.ListViewSubItem(), + new ListViewItem.ListViewSubItem(), + new ListViewItem.ListViewSubItem(), + new ListViewItem.ListViewSubItem() + }; + + /// + /// Cache of previously-constructed ListViewItems. The ListView will request items + /// continuously as they are moused-over, so this is fairly important. + /// + private ListViewItem[] mItemCache; + private int mItemCacheFirst; + + /// + /// Clears the contents of the ListViewItem cache. Do this whenever the backing + /// store is updated. + /// + private void ClearCodeListViewCache() { + mItemCache = null; + mItemCacheFirst = -1; + } + + //private ListViewItem mDummy; + private void codeListView_RetrieveVirtualItem(object sender, + RetrieveVirtualItemEventArgs e) { + //Debug.WriteLine("Retrieve " + e.ItemIndex); + //if (mDummy == null) { + // mDummy = new ListViewItem(); + // mDummy.Text = "dummy"; + // mDummy.SubItems.AddRange(mBlankArray); + //} + //e.Item = mDummy; + //return; + + // Is item cached? + if (mItemCache != null && e.ItemIndex >= mItemCacheFirst && + e.ItemIndex < mItemCacheFirst + mItemCache.Length) { + // Yes, return existing item. + e.Item = mItemCache[e.ItemIndex - mItemCacheFirst]; + } else { + // No, create item. + e.Item = CreateCodeListViewItem(e.ItemIndex); + } + } + + private void codeListView_CacheVirtualItems(object sender, CacheVirtualItemsEventArgs e) { + if (mItemCache != null && e.StartIndex >= mItemCacheFirst && + e.EndIndex <= mItemCacheFirst + mItemCache.Length) { + // Already have this span cached. + //Debug.WriteLine("LVICache " + e.StartIndex + " - " + e.EndIndex + + // " already cached"); + return; + } + + //Debug.WriteLine("LVICache " + e.StartIndex + " - " + e.EndIndex + " generating"); + mItemCacheFirst = e.StartIndex; + int len = e.EndIndex - e.StartIndex + 1; // end is inclusive + mItemCache = new ListViewItem[len]; + for (int i = 0; i < len; i++) { + mItemCache[i] = CreateCodeListViewItem(e.StartIndex + i); + } + } + + private ListViewItem CreateCodeListViewItem(int index) { + DisplayList.Line line = mDisplayList[index]; + DisplayList.FormattedParts parts = mDisplayList.GetFormattedParts(index); + ListViewItem lvi = new ListViewItem(); + + if (line.LineType == DisplayList.Line.Type.Blank) { + // no sub-items + lvi.Text = String.Empty; + lvi.SubItems.AddRange(mBlankArray); + } else if (line.LineType == DisplayList.Line.Type.LongComment || + line.LineType == DisplayList.Line.Type.Note) { + lvi.Text = parts.Comment.Replace("&", "&&"); + lvi.SubItems.AddRange(mBlankArray); + } else { + lvi.Text = parts.Offset; + mSubArray[0] = new ListViewItem.ListViewSubItem(lvi, parts.Addr); + mSubArray[1] = new ListViewItem.ListViewSubItem(lvi, parts.Bytes); + mSubArray[2] = new ListViewItem.ListViewSubItem(lvi, parts.Flags); + mSubArray[3] = new ListViewItem.ListViewSubItem(lvi, parts.Attr); + mSubArray[4] = new ListViewItem.ListViewSubItem(lvi, parts.Label); + mSubArray[5] = new ListViewItem.ListViewSubItem(lvi, parts.Opcode); + mSubArray[6] = new ListViewItem.ListViewSubItem(lvi, + parts.Operand.Replace("&", "&&")); + mSubArray[7] = new ListViewItem.ListViewSubItem(lvi, parts.Comment == null ? + string.Empty : parts.Comment.Replace("&", "&&")); + Debug.Assert(CodeListColumnWidths.NUM_COLUMNS - 1 == 8); + lvi.SubItems.AddRange(mSubArray); + } + return lvi; + } + + #endregion codeListView Virtual + + + #region symbolListView Virtual and UI handling + + /// + /// Cache of previously-constructed ListViewItems. The ListView will request items + /// continuously as they are moused-over, so this is fairly important. + /// + private ListViewItem[] mSymbolItemCache; + private int mSymbolItemCacheFirst; + + // Temporary array, used during ListViewItem creation. + private ListViewItem.ListViewSubItem[] mSymbolSubArray = + new ListViewItem.ListViewSubItem[2]; + + private string[] mSymbolColumnHeaderNames; + + private void InitSymbolListView() { + // Save a copy of the column header names as entered in the designer. + mSymbolColumnHeaderNames = new string[3]; + mSymbolColumnHeaderNames[0] = symbolTypeColumnHeader.Text; + mSymbolColumnHeaderNames[1] = symbolNameColumnHeader.Text; + mSymbolColumnHeaderNames[2] = symbolValueColumnHeader.Text; + SetSymbolColumnHeaders(); + } + + /// + /// Clears the contents of the ListViewItem cache. Do this whenever the backing + /// store is updated. + /// + private void ClearSymbolListViewCache() { + mSymbolItemCache = null; + mSymbolItemCacheFirst = -1; + } + + private void symbolListView_RetrieveVirtualItem(object sender, + RetrieveVirtualItemEventArgs e) { + // Is item cached? + if (mSymbolItemCache != null && e.ItemIndex >= mSymbolItemCacheFirst && + e.ItemIndex < mSymbolItemCacheFirst + mSymbolItemCache.Length) { + // Yes, return existing item. + e.Item = mSymbolItemCache[e.ItemIndex - mSymbolItemCacheFirst]; + } else { + // No, create item. + e.Item = CreateSymbolListViewItem(e.ItemIndex); + } + } + + private void symbolListView_CacheVirtualItems(object sender, CacheVirtualItemsEventArgs e) { + if (mSymbolItemCache != null && e.StartIndex >= mSymbolItemCacheFirst && + e.EndIndex <= mSymbolItemCacheFirst + mSymbolItemCache.Length) { + // Already have this span cached. + //Debug.WriteLine("LVICache " + e.StartIndex + " - " + e.EndIndex + + // " already cached"); + return; + } + + //Debug.WriteLine("LVICache " + e.StartIndex + " - " + e.EndIndex + " generating"); + mSymbolItemCacheFirst = e.StartIndex; + int len = e.EndIndex - e.StartIndex + 1; // end is inclusive + mSymbolItemCache = new ListViewItem[len]; + for (int i = 0; i < len; i++) { + mSymbolItemCache[i] = CreateSymbolListViewItem(e.StartIndex + i); + } + } + + private ListViewItem CreateSymbolListViewItem(int index) { + Symbol sym = mSymbolSubset.GetSubsetItem(index); + ListViewItem lvi = new ListViewItem(); + + lvi.Text = sym.SourceTypeString; + mSymbolSubArray[0] = new ListViewItem.ListViewSubItem(lvi, sym.Label); + mSymbolSubArray[1] = new ListViewItem.ListViewSubItem(lvi, + mOutputFormatter.FormatHexValue(sym.Value, 0)); + lvi.SubItems.AddRange(mSymbolSubArray); + return lvi; + } + + private void InvalidateSymbolListView() { + symbolListView.BeginUpdate(); + ClearSymbolListViewCache(); + symbolListView.VirtualListSize = mSymbolSubset.GetSubsetCount(); + symbolListView.EndUpdate(); + } + + // Column header click. Update sort. + private void symbolListView_ColumnClick(object sender, ColumnClickEventArgs e) { + //Debug.WriteLine("Click on " + e.Column); + + SymbolTableSubset.SortCol prevCol = mSymbolSubset.SortColumn; + // SortCol happens to match the ListView column numbers, so just cast it + SymbolTableSubset.SortCol clickCol = (SymbolTableSubset.SortCol)e.Column; + + if (prevCol == clickCol) { + mSymbolSubset.SortAscending = !mSymbolSubset.SortAscending; + } else { + mSymbolSubset.SortColumn = clickCol; + } + + SetSymbolColumnHeaders(); + InvalidateSymbolListView(); + } + + private void symbolListView_MouseDoubleClick(object sender, MouseEventArgs e) { + ListViewHitTestInfo info = symbolListView.HitTest(e.X, e.Y); + int row = info.Item.Index; + Symbol sym = mSymbolSubset.GetSubsetItem(row); + + if (sym.SymbolSource == Symbol.Source.Auto || sym.SymbolSource == Symbol.Source.User) { + int offset = mProject.FindLabelByName(sym.Label); + if (offset >= 0) { + GoToOffset(offset, false, true); + codeListView.Focus(); + } else { + Debug.WriteLine("DClick symbol: " + sym + ": label not found"); + } + } else { + Debug.WriteLine("DClick symbol: " + sym + ": not label"); + } + } + + /// + /// Sets the ListView column headers, adding a glyph to show sort direction. + /// Sadly, this is significantly easier than adding a graphic. + /// + private void SetSymbolColumnHeaders() { + SymbolTableSubset.SortCol sortCol = mSymbolSubset.SortColumn; + + // Pick a pair of symbols. + string sortStr = mSymbolSubset.SortAscending ? + "\u25b2" : "\u25bc"; // BLACK UP-POINTING TRIANGLE and DOWN- + //"\u2191" : "\u2193"; // UPWARDS ARROW and DOWNWARDS ARROW + //"\u2b06" : "\u2b07"; // UPWARDS BLACK ARROW and DOWNWARDS BLACK ARROW + //"\u234d" : "\u2354"; // APL FUNCTIONAL SYMBOL QUAD DELTA and ...QUAD DEL + + symbolTypeColumnHeader.Text = + (sortCol == SymbolTableSubset.SortCol.Type ? sortStr : "") + + mSymbolColumnHeaderNames[0]; + symbolNameColumnHeader.Text = + (sortCol == SymbolTableSubset.SortCol.Name ? sortStr : "") + + mSymbolColumnHeaderNames[1]; + symbolValueColumnHeader.Text = + (sortCol == SymbolTableSubset.SortCol.Value ? sortStr : "") + + mSymbolColumnHeaderNames[2]; + } + + private void symbolListView_ColumnWidthChanged(object sender, + ColumnWidthChangedEventArgs e) { + UpdateLastSymbolColumnWidth(); + } + private void symbolListView_SizeChanged(object sender, EventArgs e) { + UpdateLastSymbolColumnWidth(); + } + private void UpdateLastSymbolColumnWidth() { + const int ADJ = 4; // fudge factor needed to prevent horizontal scrollbar + int leftWidths = symbolListView.Columns[0].Width + symbolListView.Columns[1].Width; + int lastWidth = symbolListView.Size.Width - leftWidths - ADJ; + if (lastWidth < 0) { + lastWidth = 0; + } + symbolListView.Columns[2].Width = lastWidth; + AppSettings.Global.Dirty = true; + } + + private void SerializeSymbolColumnWidths() { + int[] values = new int[] { + symbolListView.Columns[0].Width, + symbolListView.Columns[1].Width, + symbolListView.Columns[2].Width + }; + AppSettings.Global.SetString(AppSettings.SYMWIN_COL_WIDTHS, + TextUtil.SerializeIntArray(values)); + } + + private void DeserializeSymbolColumnWidths() { + string str = AppSettings.Global.GetString(AppSettings.SYMWIN_COL_WIDTHS, null); + if (!string.IsNullOrEmpty(str)) { + int[] values = TextUtil.DeserializeIntArray(str); + if (values.Length == symbolListView.Columns.Count) { + for (int i = 0; i < values.Length; i++) { + symbolListView.Columns[i].Width = values[i]; + } + } + } + // The updates should automatically trigger the last-column-width adjuster. + } + + private void symbolUserCheckBox_CheckedChanged(object sender, EventArgs e) { + mSymbolSubset.IncludeUserLabels = symbolUserCheckBox.Checked; + InvalidateSymbolListView(); + } + private void symbolProjectCheckBox_CheckedChanged(object sender, EventArgs e) { + mSymbolSubset.IncludeProjectSymbols = symbolProjectCheckBox.Checked; + InvalidateSymbolListView(); + } + private void symbolPlatformCheckBox_CheckedChanged(object sender, EventArgs e) { + mSymbolSubset.IncludePlatformSymbols = symbolPlatformCheckBox.Checked; + InvalidateSymbolListView(); + } + private void symbolAutoCheckBox_CheckedChanged(object sender, EventArgs e) { + mSymbolSubset.IncludeAutoLabels = symbolAutoCheckBox.Checked; + InvalidateSymbolListView(); + } + private void symbolAddrCheckBox_CheckedChanged(object sender, EventArgs e) { + mSymbolSubset.IncludeAddresses = symbolAddressCheckBox.Checked; + InvalidateSymbolListView(); + } + private void symbolConstantCheckBox_CheckedChanged(object sender, EventArgs e) { + mSymbolSubset.IncludeConstants = symbolConstantCheckBox.Checked; + InvalidateSymbolListView(); + } + #endregion symbolListView Virtual and UI handling + + + #region referencesListView stuff + + private ListViewItem.ListViewSubItem[] mXrefSubArray = + new ListViewItem.ListViewSubItem[2]; + + /// + /// Updates the "references" view to reflect the current selection. + /// + /// The number of references to any given address should be relatively small, and + /// won't change without a data refresh, so there's no need for virtual items + /// or output caching. + /// + private void UpdateReferenceView() { + referencesListView.BeginUpdate(); + try { + referencesListView.Items.Clear(); + + // Determine which line is selected. If it's not code, data, or an EQU + // directive, there's no data to populate. + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + if (sel.Count != 1) { + return; + } + DisplayList.Line.Type type = mDisplayList[sel[0]].LineType; + if (type != DisplayList.Line.Type.Code && + type != DisplayList.Line.Type.Data && + type != DisplayList.Line.Type.EquDirective) { + // Code, data, and platform symbol EQUs have xrefs. + return; + } + + // Find the appropriate xref set. + int offset = mDisplayList[sel[0]].FileOffset; + XrefSet xrefs; + if (offset < 0) { + int index = DisplayList.DefSymIndexFromOffset(offset); + DefSymbol defSym = mProject.ActiveDefSymbolList[index]; + xrefs = defSym.Xrefs; + } else { + xrefs = mProject.GetXrefSet(offset); + } + if (xrefs == null || xrefs.Count == 0) { + return; + } + + Asm65.Formatter formatter = mOutputFormatter; + bool showBank = !mProject.CpuDef.HasAddr16; + for (int i = 0; i < xrefs.Count; i++) { + XrefSet.Xref xr = xrefs[i]; + ListViewItem lvi = new ListViewItem(); + + string typeStr; + switch (xr.Type) { + case XrefSet.XrefType.BranchOperand: + typeStr = "branch "; + break; + case XrefSet.XrefType.InstrOperand: + typeStr = "instr "; + break; + case XrefSet.XrefType.DataOperand: + typeStr = "data "; + break; + default: + Debug.Assert(false); + typeStr = "??? "; + break; + } + + lvi.Text = formatter.FormatOffset24(xr.Offset); + mXrefSubArray[0] = new ListViewItem.ListViewSubItem(lvi, + formatter.FormatAddress(mProject.GetAnattrib(xr.Offset).Address, + showBank)); + mXrefSubArray[1] = new ListViewItem.ListViewSubItem(lvi, + (xr.IsSymbolic ? "Sym " : "Num ") + typeStr + + formatter.FormatAdjustment(-xr.Adjustment)); + lvi.SubItems.AddRange(mXrefSubArray); + + referencesListView.Items.Add(lvi); + } + } finally { + referencesListView.EndUpdate(); + } + } + + private void referencesListView_MouseDoubleClick(object sender, MouseEventArgs e) { + ListViewHitTestInfo info = referencesListView.HitTest(e.X, e.Y); + ListViewItem item = info.Item; + + // The easiest way to do this is to just parse it back out of the ListViewItem. + int offset; + try { + offset = Convert.ToInt32(item.Text.Substring(1), 16); + } catch (Exception ex) { + Debug.Assert(false, "Bad ref offset '" + item.Text + "': " + ex.Message); + return; + } + Debug.WriteLine("DClick refs, offset=+" + offset.ToString("x6")); + + // Jump to the note, and shift the focus back to the code view. + GoToOffset(offset, false, true); + codeListView.Focus(); + } + + private void referencesListView_ColumnWidthChanged(object sender, + ColumnWidthChangedEventArgs e) { + //Debug.WriteLine("CH: " + e.ColumnIndex + " " + + // referencesListView.Columns[e.ColumnIndex].Width); + UpdateLastReferencesColumnWidth(); + } + private void referencesListView_SizeChanged(object sender, EventArgs e) { + UpdateLastReferencesColumnWidth(); + } + private void UpdateLastReferencesColumnWidth() { + const int ADJ = 4; // fudge factor needed to prevent horizontal scrollbar + int leftWidths = referencesListView.Columns[0].Width + + referencesListView.Columns[1].Width; + int lastWidth = referencesListView.Size.Width - leftWidths - ADJ; + if (lastWidth < 0) { + lastWidth = 0; + } + referencesListView.Columns[2].Width = lastWidth; + AppSettings.Global.Dirty = true; + } + + private void SerializeReferencesColumnWidths() { + int[] values = new int[] { + referencesListView.Columns[0].Width, + referencesListView.Columns[1].Width, + referencesListView.Columns[2].Width + }; + AppSettings.Global.SetString(AppSettings.REFWIN_COL_WIDTHS, + TextUtil.SerializeIntArray(values)); + } + + private void DeserializeReferencesColumnWidths() { + string str = AppSettings.Global.GetString(AppSettings.REFWIN_COL_WIDTHS, null); + if (!string.IsNullOrEmpty(str)) { + int[] values = TextUtil.DeserializeIntArray(str); + if (values.Length == referencesListView.Columns.Count) { + for (int i = 0; i < values.Length; i++) { + referencesListView.Columns[i].Width = values[i]; + } + } + } + // The updates should automatically trigger the last-column-width adjuster. + } + + #endregion referencesListView stuff + + + #region notesListView Virtual and UI handling + + // I'm not expecting there to be a lot of notes, but making this virtual avoids + // having to update the item set when things change. (We could also just rebuild + // the item list after any change is applied... but if we do have a lot of notes, + // that could be much worse.) Only updating the list when the notes object changes + // would be optimal, but requires probing ChangeSets. This was easier. + + /// + /// Cache of previously-constructed ListViewItems. The ListView will request items + /// continuously as they are moused-over, so this is fairly important. + /// + private ListViewItem[] mNotesItemCache; + private int mNotesItemCacheFirst; + + // Temporary array, used during ListViewItem creation. + private ListViewItem.ListViewSubItem[] mNotesSubArray = + new ListViewItem.ListViewSubItem[1]; + + /// + /// Clears the contents of the ListViewItem cache. Do this whenever the backing + /// store is updated. + /// + private void ClearNotesListViewCache() { + mNotesItemCache = null; + mNotesItemCacheFirst = -1; + } + + private void notesListView_RetrieveVirtualItem(object sender, + RetrieveVirtualItemEventArgs e) { + // Is item cached? + if (mNotesItemCache != null && e.ItemIndex >= mNotesItemCacheFirst && + e.ItemIndex < mNotesItemCacheFirst + mNotesItemCache.Length) { + // Yes, return existing item. + e.Item = mNotesItemCache[e.ItemIndex - mNotesItemCacheFirst]; + } else { + // No, create item. + e.Item = CreateNotesListViewItem(e.ItemIndex); + } + } + + private void notesListView_CacheVirtualItems(object sender, CacheVirtualItemsEventArgs e) { + if (mNotesItemCache != null && e.StartIndex >= mNotesItemCacheFirst && + e.EndIndex <= mNotesItemCacheFirst + mNotesItemCache.Length) { + // Already have this span cached. + return; + } + + mNotesItemCacheFirst = e.StartIndex; + int len = e.EndIndex - e.StartIndex + 1; // end is inclusive + mNotesItemCache = new ListViewItem[len]; + for (int i = 0; i < len; i++) { + mNotesItemCache[i] = CreateNotesListViewItem(e.StartIndex + i); + } + } + + private ListViewItem CreateNotesListViewItem(int index) { + int offset = mProject.Notes.Keys[index]; + MultiLineComment mlc = mProject.Notes.Values[index]; + ListViewItem lvi = new ListViewItem(); + + lvi.Text = mOutputFormatter.FormatOffset24(offset); + lvi.Tag = mlc; + mNotesSubArray[0] = new ListViewItem.ListViewSubItem(lvi, + mlc.Text.Replace("\r\n", " \u2022 ")); + lvi.SubItems.AddRange(mNotesSubArray); + return lvi; + } + + private void InvalidateNotesListView() { + notesListView.BeginUpdate(); + ClearNotesListViewCache(); + if (mProject == null) { + notesListView.VirtualListSize = 0; + } else { + notesListView.VirtualListSize = mProject.Notes.Count; + } + notesListView.EndUpdate(); + } + + private void notesListView_MouseDoubleClick(object sender, MouseEventArgs e) { + ListViewHitTestInfo info = notesListView.HitTest(e.X, e.Y); + int row = info.Item.Index; + int offset = mProject.Notes.Keys[row]; + Debug.WriteLine("DClick Notes row=" + row + " offset=+" + offset.ToString("x6")); + + // Jump to the note, and shift the focus back to the code view. + GoToOffset(offset, true, true); + codeListView.Focus(); + } + + private void notesListView_DrawColumnHeader(object sender, + DrawListViewColumnHeaderEventArgs e) { + + ListView lv = e.Header.ListView; + string text = lv.Columns[e.ColumnIndex].Text; + + // Adjust rect to match standard control for 10pt fonts, and + // reserve a couple pixels at the far right end for the separator. + Rectangle rect = new Rectangle(e.Bounds.X + 3, e.Bounds.Y + 4, + e.Bounds.Width - 4, e.Bounds.Height - 4); + TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.EndEllipsis | + TextFormatFlags.SingleLine; + + TextRenderer.DrawText(e.Graphics, text, lv.Font, rect, lv.ForeColor, flags); + + Pen pen = new Pen(Color.LightGray); + e.Graphics.DrawLine(pen, e.Bounds.X + e.Bounds.Width - 1, e.Bounds.Y, + e.Bounds.X + e.Bounds.Width - 1, e.Bounds.Y + e.Bounds.Height); + } + + private void notesListView_DrawItem(object sender, + DrawListViewItemEventArgs e) { + // We have no full-width items. + } + + private void notesListView_DrawSubItem(object sender, + DrawListViewSubItemEventArgs e) { + // Draw the multi-column items here. + + ListView lv = e.Item.ListView; + ListViewItem lvi = e.Item; + MultiLineComment mlc = (MultiLineComment) lvi.Tag; + + // Set colors based on selection and focus. + if (lvi.Selected && lv.Focused && e.ColumnIndex == 0) { + e.SubItem.BackColor = SystemColors.Highlight; + e.SubItem.ForeColor = codeListView.BackColor; + } else { + if (e.ColumnIndex == 1 && mlc.BackgroundColor.ToArgb() != 0) { + e.SubItem.BackColor = mlc.BackgroundColor; + } else { + e.SubItem.BackColor = lv.BackColor; + } + e.SubItem.ForeColor = lv.ForeColor; + } + + e.DrawBackground(); + + // Shift the text so it lines up with the standard control at 10pts. + // Not strictly necessary, and possibly unwise, since the behavior seems + // to change for larger fonts. + Rectangle rect = new Rectangle(e.Bounds.X + 3, e.Bounds.Y + 2, + e.Bounds.Width - 3, e.Bounds.Height - 2); + + TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.EndEllipsis | + TextFormatFlags.SingleLine; + + Font font = lv.Font; + + // TODO(maybe): consider drawing the note text with a proportional font. We + // don't need multi-line stuff to line up, and it'll let us show more of + // the text in a narrow window. + TextRenderer.DrawText(e.Graphics, e.SubItem.Text, font, rect, + e.SubItem.ForeColor, flags); + + // Draw the focus rectangle. It's annoying that we have to draw it for every + // sub-item even with FullRowSelect, but DrawItem always happens first, and + // there's no equivalent at the end. (It's unclear how useful the focus rect + // actually is, but it's part of the standard dialog behavior.) + if (lv.FullRowSelect) { + e.DrawFocusRectangle(e.Item.Bounds); + } else { + e.DrawFocusRectangle(e.Bounds); + } + } + + private void notesListView_ColumnWidthChanged(object sender, + ColumnWidthChangedEventArgs e) { + // We don't auto-adjust column widths, but we do want to make sure that the + // width adjustment causes the settings to be saved. + AppSettings.Global.Dirty = true; + } + + private void SerializeNotesColumnWidths() { + int[] values = new int[] { + notesListView.Columns[0].Width, + notesListView.Columns[1].Width + }; + AppSettings.Global.SetString(AppSettings.NOTEWIN_COL_WIDTHS, + TextUtil.SerializeIntArray(values)); + } + + private void DeserializeNotesColumnWidths() { + string str = AppSettings.Global.GetString(AppSettings.NOTEWIN_COL_WIDTHS, null); + if (!string.IsNullOrEmpty(str)) { + int[] values = TextUtil.DeserializeIntArray(str); + if (values.Length == notesListView.Columns.Count) { + for (int i = 0; i < values.Length; i++) { + notesListView.Columns[i].Width = values[i]; + } + } + } + } + + #endregion // notesListView Virtual and UI handling + + + #region Info view + + /// + /// Updates the Info window for the current selection. + /// + private void UpdateInfoView() { + // I'm seeing weird behavior where, if you do a bunch of up-arrow/down-arrow + // movement in the main window, the info window will eventually freeze. You can + // refresh individual lines by dragging across them with the mouse. Resizing + // the splitter pane leaves the window apparently blank, but moving or resizing + // the application window causes an immediate redraw. + // + // Simply calling Invalidate() didn't help, which shouldn't be too surprising + // since I expect assignment to the Text field to do something similar. Refresh() + // feels a bit heavy-handed but it gets the job done. + // + // I see similar behavior from RichTextBox. The References ListView, which is + // also updated when the selection changes, seems to work correctly though. + // + // This is on Win10 Pro x64, as of 2018/07/28. + DoUpdateInfoView(); + infoTextBox.Refresh(); + } + private void DoUpdateInfoView() { + ListView.SelectedIndexCollection sel = codeListView.SelectedIndices; + if (sel.Count != 1) { + // Nothing selected, or multiple lines selected. + // Calling here from SelectedIndexChanged events works fine for single-item + // selections, but the SelectedIndices list is always empty for multi-select. + // If we add this to other selection events it ends up getting called 3-4 + // times when you arrow around. + + //infoRichTextBox.Text = "(" + + // string.Format(Properties.Resources.FMT_LINES_SELECTED, sel.Count) + ")"; + infoTextBox.Text = string.Empty; + return; + } + int lineIndex = sel[0]; + DisplayList.Line line = mDisplayList[lineIndex]; + StringBuilder sb = new StringBuilder(250); + + // NOTE: this should be made easier to localize + string lineTypeStr; + string extraStr = string.Empty; + switch (line.LineType) { + case DisplayList.Line.Type.Code: + lineTypeStr = "code"; + break; + case DisplayList.Line.Type.Data: + if (mProject.GetAnattrib(line.FileOffset).IsInlineData) { + lineTypeStr = "inline data"; + } else { + lineTypeStr = "data"; + } + break; + case DisplayList.Line.Type.LongComment: + lineTypeStr = "comment"; + break; + case DisplayList.Line.Type.Note: + lineTypeStr = "note"; + break; + case DisplayList.Line.Type.Blank: + lineTypeStr = "blank line"; + //lineTypeStr = "blank line (+" + + // mOutputFormatter.FormatOffset24(line.FileOffset) + ")"; + break; + case DisplayList.Line.Type.OrgDirective: + lineTypeStr = "address directive"; + break; + case DisplayList.Line.Type.RegWidthDirective: + lineTypeStr = "register width directive"; + break; + case DisplayList.Line.Type.EquDirective: { + lineTypeStr = "equate"; + int symIndex = DisplayList.DefSymIndexFromOffset(line.FileOffset); + DefSymbol defSym = mProject.ActiveDefSymbolList[symIndex]; + string sourceStr; + if (defSym.SymbolSource == Symbol.Source.Project) { + sourceStr = "project symbol definition"; + } else if (defSym.SymbolSource == Symbol.Source.Platform) { + sourceStr = "platform symbol file"; + } else { + sourceStr = "???"; + } + extraStr = "Source: " + sourceStr; + } + break; + default: + lineTypeStr = "???"; + break; + } + + // For anything that isn't code or data, show something simple and bail. + if (line.OffsetSpan == 0) { + sb.AppendFormat(Properties.Resources.FMT_INFO_LINE_SUM_NON, + lineIndex, lineTypeStr); + if (!string.IsNullOrEmpty(extraStr)) { + sb.Append("\r\n\r\n"); + sb.Append(extraStr); + } + infoTextBox.Text = sb.ToString(); + return; + } + Debug.Assert(line.IsCodeOrData); + + Anattrib attr = mProject.GetAnattrib(line.FileOffset); + + // Show number of bytes of code/data. + if (line.OffsetSpan == 1) { + sb.AppendFormat(Properties.Resources.FMT_INFO_LINE_SUM_SINGULAR, + lineIndex, line.OffsetSpan, lineTypeStr); + } else { + sb.AppendFormat(Properties.Resources.FMT_INFO_LINE_SUM_PLURAL, + lineIndex, line.OffsetSpan, lineTypeStr); + } + sb.Append("\r\n"); + + if (!mProject.OperandFormats.TryGetValue(line.FileOffset, out FormatDescriptor dfd)) { + // No user-specified format, but there may be a generated format. + sb.AppendFormat(Properties.Resources.FMT_INFO_FD_SUM, + Properties.Resources.DEFAULT_VALUE); + if (attr.DataDescriptor != null) { + sb.Append(" ["); + sb.Append(attr.DataDescriptor.ToUiString()); + sb.Append("]"); + } + } else { + // User-specified operand format. + // If the descriptor has a weak reference to an unknown symbol, should we + // call that out here? + sb.AppendFormat(Properties.Resources.FMT_INFO_FD_SUM, dfd.ToUiString()); + } + sb.Append("\r\n"); + + // Debug only + //sb.Append("DEBUG: opAddr=" + attr.OperandAddress.ToString("x4") + + // " opOff=" + attr.OperandOffset.ToString("x4") + "\r\n"); + + sb.Append("\u2022Attributes:"); + if (attr.IsHinted) { + sb.Append(" Hinted("); + for (int i = 0; i < line.OffsetSpan; i++) { + switch (mProject.TypeHints[line.FileOffset + i]) { + case CodeAnalysis.TypeHint.Code: + sb.Append("C"); + break; + case CodeAnalysis.TypeHint.Data: + sb.Append("D"); + break; + case CodeAnalysis.TypeHint.InlineData: + sb.Append("I"); + break; + default: + break; + } + if (i > 8) { + sb.Append("..."); + break; + } + } + sb.Append(')'); + } + if (attr.IsEntryPoint) { + sb.Append(" EntryPoint"); + } + if (attr.IsBranchTarget) { + sb.Append(" BranchTarget"); + } + if (attr.DoesNotContinue) { + sb.Append(" NoContinue"); + } + if (attr.DoesNotBranch) { + sb.Append(" NoBranch"); + } + if (mProject.StatusFlagOverrides[line.FileOffset].AsInt != 0) { + sb.Append(" StatusFlags"); + } + sb.Append("\r\n\r\n"); + + if (attr.IsInstruction) { + Asm65.OpDef op = mProject.CpuDef.GetOpDef(mProject.FileData[line.FileOffset]); + + string shortDesc = mOpDesc.GetShortDescription(op.Mnemonic); + if (!string.IsNullOrEmpty(shortDesc)) { + if (op.IsUndocumented) { + sb.Append("\u23e9[*] "); + } else { + sb.Append("\u23e9 "); + } + sb.Append(shortDesc); + string addrStr = mOpDesc.GetAddressModeDescription(op.AddrMode); + if (!string.IsNullOrEmpty(addrStr)) { + sb.Append(", "); + sb.Append(addrStr); + } + sb.Append("\r\n"); + } + + sb.Append("\u2022Cycles: "); + int cycles = op.Cycles; + Asm65.OpDef.CycleMod cycMods = op.CycleMods; + sb.Append(cycles.ToString()); + if (cycMods != 0) { + sb.Append(" ("); + int workBits = (int)cycMods; + while (workBits != 0) { + // Isolate rightmost bit. + int firstBit = (~workBits + 1) & workBits; + sb.Append(mOpDesc.GetCycleModDescription((OpDef.CycleMod)firstBit)); + // Remove from set. + workBits &= ~firstBit; + if (workBits != 0) { + // more to come + sb.Append(", "); + } + } + sb.Append(")"); + } + sb.Append("\r\n"); + + const string FLAGS = "NVMXDIZC"; + sb.Append("\u2022Flags affected: "); + Asm65.StatusFlags affectedFlags = op.FlagsAffected; + for (int i = 0; i < 8; i++) { + if (affectedFlags.GetBit((StatusFlags.FlagBits)(7 - i)) >= 0) { + sb.Append(' '); + sb.Append(FLAGS[i]); + } else { + sb.Append(" -"); + } + } + sb.Append("\r\n"); + + string longDesc = mOpDesc.GetLongDescription(op.Mnemonic); + if (!string.IsNullOrEmpty(longDesc)) { + sb.Append("\r\n"); + sb.Append(longDesc); + sb.Append("\r\n"); + } + } else { + // do we want descriptions of the pseudo-ops? + } + + + // Publish + infoTextBox.Text = sb.ToString(); + } + + #endregion // Info view + + + #region Tools items + + private void hexDumpToolStripMenuItem_Click(object sender, EventArgs e) { + OpenFileDialog fileDlg = new OpenFileDialog(); + fileDlg.Filter = Properties.Resources.FILE_FILTER_ALL; + fileDlg.FilterIndex = 1; + if (fileDlg.ShowDialog() != DialogResult.OK) { + return; + } + string fileName = fileDlg.FileName; + FileInfo fi = new FileInfo(fileName); + if (fi.Length > Tools.HexDumpViewer.MAX_LENGTH) { + string msg = string.Format(Properties.Resources.ERR_FILE_TOO_LARGE, + Tools.HexDumpViewer.MAX_LENGTH); + MessageBox.Show(msg, Properties.Resources.OPEN_DATA_FAIL_CAPTION, + MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + byte[] data; + try { + data = File.ReadAllBytes(fileName); + } catch (Exception ex) { + MessageBox.Show(ex.Message); + return; + } + + // Fire and forget. + Tools.HexDumpViewer dlg = new Tools.HexDumpViewer(data, mOutputFormatter); + dlg.Text = Path.GetFileName(fileName); + dlg.Show(); + } + + #endregion Tools items + + + #region Debug menu items + + private void dEBUGToolStripMenuItem_DropDownOpened(object sender, EventArgs e) { + toggleOwnerDrawToolStripMenuItem.Checked = codeListView.OwnerDraw; + toggleCommentRulersToolStripMenuItem.Checked = MultiLineComment.DebugShowRuler; + useKeepAliveHackToolStripMenuItem.Checked = ScriptManager.UseKeepAliveHack; + } + + private void reanalyzeToolStripMenuItem_Click(object sender, EventArgs e) { + Debug.WriteLine("Reanalyzing..."); + // Call through ApplyChanges so we update the timer task output. + UndoableChange uc = + UndoableChange.CreateDummyChange(UndoableChange.ReanalysisScope.CodeAndData); + ApplyChanges(new ChangeSet(uc), false); + UpdateMenuItemsAndTitle(); // in case something changed + } + + private Tools.ShowText mShowUndoRedoHistoryDialog; + + private void showUndoRedoHistoryToolStripMenuItem_Click(object sender, EventArgs e) { + // Show or hide the modeless dialog. + if (mShowUndoRedoHistoryDialog == null) { + Tools.ShowText dlg = new Tools.ShowText(); + dlg.Title = "Undo/Redo History"; + dlg.BodyText = mProject.DebugGetUndoRedoHistory(); + dlg.OnWindowClosing += (arg) => { + Debug.WriteLine("Undo/redo dialog closed"); + showUndoRedoHistoryToolStripMenuItem.Checked = false; + mShowUndoRedoHistoryDialog = null; + }; + dlg.Show(); + mShowUndoRedoHistoryDialog = dlg; + showUndoRedoHistoryToolStripMenuItem.Checked = true; + } else { + // Ask the dialog to close. Do the cleanup in the event. + mShowUndoRedoHistoryDialog.Close(); + } + } + + private Tools.ShowText mShowAnalyzerOutputDialog; + + private void showAnalyzerOutputToolStripMenuItem_Click(object sender, EventArgs e) { + // Show or hide the modeless dialog. + if (mShowAnalyzerOutputDialog == null) { + Tools.ShowText dlg = new Tools.ShowText(); + dlg.Title = "Analyzer Output"; + if (mGenerationLog == null) { + dlg.BodyText = "(no data yet)"; + } else { + dlg.BodyText = mGenerationLog.WriteToString(); + } + dlg.OnWindowClosing += (arg) => { + Debug.WriteLine("Analyzer output dialog closed"); + showAnalyzerOutputToolStripMenuItem.Checked = false; + mShowAnalyzerOutputDialog = null; + }; + dlg.Show(); + mShowAnalyzerOutputDialog = dlg; + showAnalyzerOutputToolStripMenuItem.Checked = true; + } else { + // Ask the dialog to close. Do the cleanup in the event. + mShowAnalyzerOutputDialog.Close(); + } + } + + private Tools.ShowText mShowAnalysisTimersDialog; + + private void showAnalysisTimersToolStripMenuItem_Click(object sender, EventArgs e) { + // Show or hide the modeless dialog. + if (mShowAnalysisTimersDialog == null) { + Tools.ShowText dlg = new Tools.ShowText(); + dlg.Title = "Analysis Timers"; + dlg.BodyText = "(no data yet)"; + dlg.OnWindowClosing += (arg) => { + Debug.WriteLine("Analysis timers dialog closed"); + showAnalysisTimersToolStripMenuItem.Checked = false; + mShowAnalysisTimersDialog = null; + }; + dlg.Show(); + mShowAnalysisTimersDialog = dlg; + showAnalysisTimersToolStripMenuItem.Checked = true; + } else { + // Ask the dialog to close. Do the cleanup in the event. + mShowAnalysisTimersDialog.Close(); + } + } + + private void toggleOwnerDrawToolStripMenuItem_Click(object sender, EventArgs e) { + Debug.WriteLine("TOGGLE OWNERDRAW"); + bool newState = !codeListView.OwnerDraw; + codeListView.OwnerDraw = newState; + notesListView.OwnerDraw = newState; + } + + private void toggleCommentRulersToolStripMenuItem_Click(object sender, EventArgs e) { + MultiLineComment.DebugShowRuler = !MultiLineComment.DebugShowRuler; + // Don't need to repeat the analysis, but we do want to save/restore the + // selection and top position when the comment fields change size. + UndoableChange uc = + UndoableChange.CreateDummyChange(UndoableChange.ReanalysisScope.DataOnly); + ApplyChanges(new ChangeSet(uc), false); + } + + private void useKeepAliveHackToolStripMenuItem_Click(object sender, EventArgs e) { + ScriptManager.UseKeepAliveHack = !ScriptManager.UseKeepAliveHack; + } + + private void sourceGenTestsToolStripMenuItem_Click(object sender, EventArgs e) { + Tests.GenTestRunner dlg = new Tests.GenTestRunner(); + dlg.ShowDialog(); + dlg.Dispose(); + } + + private void extensionScriptInfoToolStripMenuItem_Click(object sender, EventArgs e) { + string info = mProject.DebugGetLoadedScriptInfo(); + + Tools.ShowText dlg = new Tools.ShowText(); + dlg.Title = "Loaded Extension Script Info"; + dlg.BodyText = info; + dlg.ShowDialog(); + } + + #endregion Debug menu items + } +} diff --git a/SourceGen/AppForms/ProjectView.resx b/SourceGen/AppForms/ProjectView.resx new file mode 100644 index 0000000..666c9b1 --- /dev/null +++ b/SourceGen/AppForms/ProjectView.resx @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 132, 17 + + + 269, 17 + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAA8SURBVDhPY8AHOGPO/ocySQcgzWQbANNMlgHImkk2YJho + JhVTbgA2V4AFSQXD2BCoEOmAYgNAgLABDAwANqCmjcoYW+kAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAA5SURBVDhPYyAEOGPO/ocyyQMgAygyBGYA2YYgGzBcDUEX + JBVTbgApYCRqBgGKNIMARZpBAL9mBgYAlSmmjRN9gzIAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAERSURBVDhPrZDbSgJRGIXnpewd6jXsjSQvIrwoI0RQMChU + 0iiDPCGiE3ZCRkvR8VzTeBhnyR5/ccaZNnPhB4t9sdf6Ln5hb8QeathNJFVFKF5C8DqL4ksDVHWGDf7j + LHyPg6NjviSaFqlu5yQYR+KpupaIkrMknCxT3Y7v/NYYb0ITK1c3BarbWWhLQ7IR0cTKReyZ6lZ0XYei + ztHpK4bAc+h1FgQijzSxMptrGIxVSO0xX3AaStFki7bUMVFmaMm/eJMGfIH/MkGzLep0AXn4h/r3CJV3 + mS9gn2bY4UY/UzQ7E9TqfeTFtnuB+XAfzSHKr11kSl/uBebDiZ89ZCst3OUkdwL28sIVsE83ock+EIQV + 2Mz2wxeg6/UAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJHSURBVDhPxZBdSNNhFMb/F110ZZEVhVBgeeHNICiiuggp + olAUyyxI0oSaH1QYC3N+tKnp5ubm1JUua5uuqdNKMwr7kApFItTUkWZqVhSVYmao5Nevvy7UoYR3HXh4 + 4XCe33nOKyy3lAY7l9RWMo0O/raWXxEyo5spVYTNvOGyfIRPfW+ptOkXqaPl6T83hcRmExSdgzAz3NVm + YWyoYla/B+1M9JtxWLPpaH22JORIjI6gKAMB0jyEimIdo4OlbuaprwVMOOMovammpDADc34qppwUrmnl + 5Kni3aFlFg2j3y1z5mnRTJccnNIltQhwq0jFry+mOXNtpWZWDx1Z1NhV3C3JwGFOw25SYjVe5oYhiUKd + HKMmwQUrMWUw/CF3NnZvvYKqUh1TvUroS3fXe7HXkwidMngTS2t5KLbregSzMY2f3Wr4qKW6LJvGR1rX + 0MLor8OhKYTJBn/GHvvxrliCTBrsOqXIoOBHh5K+hmSq7FqmexTQHuUytkaKxuNMNgYyVneA4Qd7GKjc + hjLaRzxH7gIU6JIZaEvgtk1D8wsxSWecCDgNzWFMvwxm/PkhRmr3Mli1nW9lvjRdWc0Jf+/5jzRmyWmv + S+GOLQu6U6BFjPvqKOP1AYw88WOoZif9DgmfLVtxaj1RSLdwNvrkPCA3M54KqxrnvRia9MKcGrUrqFOt + 5H7qKsqT1mGO9+Lqhc2ELdw+U/r0i+gVZ8hMiCDx3DHORwZyKnQ/hw/uYt9uCTskPvh6e7Fp41rWr/Fg + g6eHO+A/lyD8ARfG3mk9fv1YAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIySURBVDhPrZLfS5NRGMfff6H7boIuuq2pMZyL1eAt11CW + DcOKsB9vpFmaLtNExco0av6CbIVLJ61Wk3BSkT/AFCkRZSpZmrmiJQ41xSaCwdfznL15XEUX0Reem5f3 + 8znnec4j/Zc8fxYGla91CS3eRTx0z6OpMYS7jmnU1X6B/VYA18snUVoyjsKCt8jLHcH5c36ouCQR2NUJ + 1Nas4G9ZXlmFKbULh1Kf8lJxSfI+WeCCyopv6q+/h+DQ/DJ2WV5Ao1FgPegRAveDOS4oLfmq/h6dn/DH + 4AJizD4UXJrCAUuzEDgbZrjgou2DiohshIcnQtgme5GTPYbkJKcQ1N8OckHW2REVi+RXuM8fxGaDG4oy + ALPZIQQ11Z+5QDk1oKJ/hjv7P2FTfCMOH3mFxMQ6IbhROYWOdrCnBI4dfwPr0V4+bRoY9UzXppMjcDdS + rC8hy3YhuFI2gTYf2A4Aza4f7N2/o/zaLB8qDYx6zszwr8P7k1thNFYIweXCMXgeAfedq2xxwjClZUeV + Jd2GtDNFETiJwfs8MBjKhMCWN8pgoLoqzE8miH1GjE7G4PsZjE7OQsm9ij2mFg7rdrug1xcJAa2l4w7W + r00Cgk/n38S7wBwC04u4UGxHrMHF4CbEJtyDLj5fCDIzhljfSxzeavRgyw4Zj9t64GvvQ0d3P3pfD2Kv + 2QqNvgFxDN6urYdWmyMElJMnevh60obRktA701PRtGlg1DOdSkXwzrisaMG/RZLWAE60OMW5fNhvAAAA + AElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIpSURBVDhPtZL/T1JRGMb5p1itrVZbbRpqZbawnBENV1I0 + jGlByTSyJTXJwq2oKZQb1KAv6JCYWSxvBrkkZUq4CeQEiRABFeLL072Xa0zRra31bO8v57zP5znnPYf1 + X+TxhWF6O7VtGYcnwbSWijKPOLzYrPSvLPwLS3huGUMlT7o9wGD9grVUBj+icdid03S9tDmgNxNwTgVQ + J+rA8XNtWwM+uuZATMwxmQVRycuJFNyzIRitDlScugKzjSgFRGJJaIwEsrk8AsHIhnSL/Ssck37UNipQ + I5DjtuYV7uksRYhr2kebhx2eP6nrycFIEh5fBA/1Nvru8q5+PDaOovK0rABwfwugWzcErfkzHhjsePL6 + E7q1VrTdNUDcrgGvSYlDZHN5XTNOnL8BVe8AJAoNDtZfLgDu9L1BPJmikzcrk81hlRwodZJwdBXziwnI + OrVoaOkiT8C8hKLHBPO7CbywOaE1jeC+bhAd6meQdvZC1KoG/5IS3MZ2HObLUHZSggvkWq3wOvbWiAqA + VpWeyStVfCUNf3AZ4zNhfHCFMEDMgye+hYr6FrDLzxQAUuVTpr0ocn74mchg5vsKRt1RcHp2Qv9+kZ78 + UcE17KkWFgHNN/uQzgBkGKLJPBZiecyGchjzrmFwPIF++xJUbDbUQzEacIArLpopSRSP4CUN1Obf1Abz + uqob5KjiXwWH/GVl5HPt5zZh37GL2H1EiF1VZ7GDI6CNW5r/TSzWbwHYL0mKJ5czAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGDSURBVDhPrZFNSwJRGIX9NYGbFoUlFElY1EJQKEYhCJsi + LaVsERnRF5iCaSZJO1toCDVGFkgoFpWQWWRR2aIvUxm1BKN1wSnHCFw4TOCzue+9nPNw4eVVnav4Izzb + QfxeGZ5TWaxT/rK3irzmC7CsusvC1G4IkbNLboIiDieF4GGUKeTeClDpppF8eeEu2PIfwfrzizSdw3Hk + EnKlFpkMzV2wH77AosOFTV8A+vkl9CiHuJeLJNNZjM8tYWB0FkTvMAwmy/8ERTR6CwjlGAi1Ccence6C + 1NsXzN4PKIxJLLgeIJ2MoXvmFraNBKK3eXZRIveJPvs7FIYniEkXZENOdE+GIZ2Ko10TwLK7tJmKmL0F + EEYarYM+NMnt0C1sQzpx/lcSEnZ2gcKY/gs0dlmZuWvmjjmpwA1qxVp2AWFIMAF/OAGBzMjMI7ZrtJCb + 4Df3o4Zfxy7QrdxDRFKol5khkpR2H4qmIOzUQNBGwrsXYxccnNOQqNbQ0KGGZ+eEPVwdeLxvqqrf4wGh + TNAAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHkSURBVDhPvZHfS1NhHIf3p5QypLr2D4goMwoMCi/qIugH + Xe1Cr7qKDIMkZixwNhfWLGWbnuki0kXKzLU023KubBNPJrbRdOzocm6e2dPOO21mMS+CHvjcvOf9PF++ + 79H9M+7RT2iRRsIi9sEAXe43yAvf2LpSHq28G9uAnytNT4jMLewtcQ2Ht2pF8ps/aOt+gccX5lxD694S + +1BQFD1RkN5DSFa4Z3uONKbgHE3h8KZ4OJTC1J8UiSzmfhd2uf1CoJHbyKOsZokl0kKwm+aeJaov+wjO + rpQkVqdXfOz0bWAcVLghfaXxkUz3y2VxvpMGSwL3uMKh+gHezSSLEnNhX23vtYzKUirDfGyFj/Iy1mdx + UWqR8iKhwtQLxjgH659y4EwvVXWPiwJt3/Ws+muywRrlqvkDdx3zQrCN8l1ldnEd3/QqFmkS/akHJYGS + zjLzOUEwEsMf+sLI2zmaOou/93pPGoM5zvk7UU7fnBKxSBPoT7SXBNW1F/9Io2lKCNTCeomUyrS8xnBA + wfUqyf1eP5U1ptJD/o1LzeNCsHPydtqdr6k4aiwvOHvNSya3ibU/QIdrEkvfhJislc32MfYfuV1eUGPw + FF7bIVJVZ0N/soPK421UHGstlFvYd/hWecF/Qqf7CR0A5wwgSQA2AAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJSSURBVDhPtZJrSJNRGMdf6IN9KbpQn/pUEH2JIoLqQ0Zh + FqYZRmJG1iKmUqKyLB2pqSm6vC1Nm5GXoeatEsVJ0RASR3eNzegikRq5lrV3857Fr/d9ddlICoL+8OfA + Oef/e57zcIT/os7WLMw302muSGJ2689qqi7A44q8IzjtNYzarzHQm8tZtT8FmRqu6LToMxN+B8qhCbGR + KVcDE85ajKUaxoaryEuL4UVXIudPB5Ko2oy98xjDptXERuz3hsgAOTzlqqMk6yjdllzE90UM9Wp5azlB + S1kwkeG+1CSv4mmBQPThfd6Ahqq8GYB4A11yBKmaMLQxoZyLDkGjDiZOFUhUuB+FsWsUQFiArzegtlzH + pFjPpMPA2GA2jucx2KqWK7ZWLqO7dBGP9D5KWLbfto3eAKMhi3FHBeP9GYy9PMXos4OIrYvJrzSRbWjm + wuV6EnVG4tLLiEzSExGf4w0oL05nZEDPaK+akceBuO9v4uPtFUrYo6npbzhdE/QPOQmNSiPouHYOUpaf + gvgqA/dDf9wd63G1r2SgUlAqyyq/1anYUGfG2mdXwne7bOwJUc1AinOS+NxzBpd5HWLbUhyNPvRdF5S2 + v05/54tbqvzBifWNHUvPOwLC4/CXwrv2HsB3+w6EwosJOB5ESeElfGpayGD1AmwlArHSm+W2PR1clToo + MrbT0mFTVtlbN6xFuJQar3wQz5Q9VksD+7XyPctrJdx4p5s605M5gKz8lJPSDwtGFbKboJ1blAN52vKb + PdXm80/AfDokTVu+8DfPXv9XCcIPTvjvLQ8YoakAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIVSURBVDhPtVJNaxNRFM1PyE+Yn1AUXLjK0uWgDWQZwUUX + KsGFBEEcCkIwqBEpGiydsSo2kupsasdo7Yi2toh0sFZjG5JpiZo20/TpVOmH5njvm8BYahEXHji8+968 + c+55l4n8F0zM+rhVWkHmdg29A/PoK1Yw8uIjOp/3xpvqBgrjLeilZbjNLXxZ34bwt6jexMVCGRndQenl + 0p+NWHzPXoP3rQ3bAbQhQM0E5Np2BKprbZzrm8TIs8puE+68+r0NwwZiacCwALEBCVcAqet8JlAjk1PZ + JzsNJt6u4+FMS3ZmMV9mmFNAMhesbBZLC6oFdOsd8oVXocmdx018Ej9k1FgqiJ0zgS6qlR6BVI4iEFRN + IJlxMF/1cfTMcGiQvbskB6ZqgairJ6BCTJKYu9tlAUW1oSRsNDwfB+JXQ4PzN6s07W0ZPxDS5aSgJEFn + 06Y9CaOqSauJRvMr9qmXQ4P8/RoWvU16eyBUEq5kbigwiKoOMTBQ0zbKlTq6TxihwejkZ1iOJwfEwmiC + BQ49yaW50J7Fh0xJw3IxbM3hwo2x0ICRHZzFgveTunYERK5lgo5YMxx8WPFw5Li+U8wYm66jNz+Naov+ + Beqiao58N5NrPluoryJO0QeKU7sNGKPPazh9aRzGo/eYmVvEMk270fTlmzl2N3XW9xL/jv7iaxw7+wAH + E9ew//AVxE8OItv/9O/Cf0ck8gud2vKswuxNZgAAAABJRU5ErkJggg== + + + + + AAABAAEAICAAAAEAGACoDAAAFgAAACgAAAAgAAAAQAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABKTUFQUkdxb2eDfnRbXFJnZ1xLTkFOUEJKTjtWWUhsaVxybWBraWJVVkpBRTZARTQ9QzhZV1NxbmZa + WE9UVUxESkU8QS48QDB0c29zcGdmaGB9eXBAQjY/Qjk/QjlQUk1SUk07PjU2OS81OC80Nyc2Oi13dG9S + VktqaWE4OSw/QTI+QDI8PzI9PjF3c2lcXVFLSz8/QjhISj4zNSc4OSx3dGxHS0JkZVleYlxeYlhYXlRg + YltrbGU8QDdeYVhLTUYaGBsfHCEmIiQsKS0jICQnIyknIykpKSckIygqJikuKi8wLDEuKS80MDUhHiEj + ICItKjEuKS41MDUpJCknIiguLDIgHB0bGSA5NS9RUEhPS0A2My0bFxwQDg8UExksKzIXFxsbGR8gHSUh + HiYiICctKjIqJy4YGRsdHSMnIioqJS0mIiouKTE/PEYfHCEWFhowLjgnIyslISknIisnIiksKzMgGyAm + JCwwLS80MjQzMTMoJikmIioPDRAWFBkjHyUUFBkaGR4iICgnJCshHiYrKjErKjMZGBocHSMrJi0rJy4o + JCstKTA5Nj8gHSIWFRsvLTcrKC8pJCsrJi4qJSwsLDQiHiQrJy81OC8/QzU7PzMgJR0oJS0REBMZGB0o + JygVFRobGR4hHyYlIysiICcmJCwtKzQcGh4aGiEwKzItKTAsKDAuKTI3Mz0hHiMWFRouKzMvKzMvKjMx + LTQpJCwsKjIlISYwKzMvMy04PTM2OjEhIx0pJy8SEBMdGiAsKywWFRkfHiUpJSsrKTAvLDMnJSwvLzgd + HSEbGyIyLjZAPEUpJS45NT01MjwhHyQXFxwtKjI/O0QvKzMyLTYtKDApJy8oJSorKDBER0NLUUtCSUQp + LSomJCsSERUeHCMyMTQTEhYcGyJmYm89OURFQkomIyo2NUAcHB8bGyIxLjU7OEM/PEZKR081MjskIScY + GB0yLjczMDg2Mjw5NT8uKjEpKTEoJCovLDQeIRchJRkYGxQMDwoqJi0UFBgdHCIjIiUSEhUiIScrJzA0 + Mjw9OUMnJCw7O0YcHCAeHiYqJi1IRU89O0U5N0M4NT4mIykZGB02Mzw5NTw6Nj81MToxLDUxMDkoIykw + Ljc3Mjc0LjQxKzIsJy0oJi0WFBcgHycqJSsSEhYoJy45NT0vLTY/O0clIyo6OUYfHyMjJCsrJy5GRFFA + PEhIRlE7OEImIigWFhw2Mzw3Mjw2MTo2MjstKDEyMjsnIicxLjY9Pzw8QDozNjQfISAqJy4XFRkkIysv + LjATExYtKzM3NDwsKTIlIisnJSw7O0ggHyIkJS0tKTIrJzAsKDAwLDQ7OEQoJSoXFxw3NT81MTg1MDo1 + MDkvKjMwMDgnIygwLDYaHhsjKiMjKCQVGBcpJS0ZFhofHiUlJSYQEBMoKDAnJCwnJCwnJC0lIyk7PEgf + HiInJy8uKTAwLDMwLDUyLTY7OUQqJisYGB06N0EzLjc2Mjo0LzcwLDQvLjYnIic1MTgjHyAkISEiHyAg + HCAmIikaFxwiISgjHyQZGRsbGiYcGSQlIi0nJS4eGiY1NUU8QD4dHioqJTAtKDUqJjIvKjQ6N0IuLDEc + HSQyLz00MDkrJzMwKzcrJzIsKTEuLDMoJDJgaGBQWFVNVlRDR0QbFyIfHiIUEh5HS0ouMjiOkFulpmm0 + sm47OzXFxXi8u2+anqbJyIGpqW+ko2eioHNXVkY7OTw5PDjJyHmqqmk9Ozy1s3GVl2Nsa1F6e1S6unPW + 1YBLT0W+vnOhpGw7QUDIyHlGTT+hpGk6QDw2Ozs2PUJlak/V1X99gVhjZktna1bIyajR0IyMj5TExY66 + u5Cxs6yFhoeZm4uvsYqOkp2XmaLV1IO5u4u/v57X1Yqcm5jBwnLCwaPQ0ZSrrHFaYEq9vXJITzWWml05 + QTM0OTqVlmKYmmZDSD9VV0a/v3a+vnVTWU3IyHw/RT2qq2uOkV6zs3HMy3g0ODnBwXOVl19ARj2wsW6Q + k2GFh1zGxndLT0RqbFBVWUeys3C5unNMUkfb2n6qrGiAg1A9RDZQUVJucE2ysm+urnhycGxeXlg5QD1I + UEwoLykxNzI7QThCRT0zNzQuMy8uNTFBRkhgYVplZmBARD1ITD5XWVhydFTDw3nBwnlBRUBTWlJfZl5Y + XlZ1dnRwcGZdX1ZJUEgPDAwmIyVQT1MwKzMXFBMZFRUzMTU/PT0hHiEjICIiHyAkICAdGhscGRwfHB4i + ICIkICMnJCQeGhogHB0pJSggHR0XExkWFBkdGhosKyVLS0MzMTEXExMSEREbGhtDREAzLzZIRUonKS4Q + ERo9Nz82Mj40MzskJCcrJy8mIisuKzAhHiQdGyIfGyMjICQjICkmIisuKS0rJy0kISofHSIeGyIlIiYf + HiYrKS4vLjAyNDYhICIkJTEpJi0XFyAaHRMwLTR8eWWxsWympmZJRUWwr3KzsnAqKDCjoWiurG5nZE+R + j2G3tXG0snBLR0K3tXGzsW43MzqgnWefnWZlYUyXlWEvKzKtq2onIiy3tm6trWseHSG3tnU3NTSFhlog + Ih8qJzDMyYo3OTSqqWZOSUs/PEeRkGJvbU/V1H5BPT++vHQoJDOxsG9BPT9bWEqGhGBoZU+EgmPOzXqR + jmFsalGioWkyLje6uXJdWkl6eVIeHyMICBS8u3M5NzSPj14RFA4uKjG5t32Skl9jZEVUTk7NzX9yclA4 + ODeYlmSNi2DFw3gnIzItKTXAvnVXU0vHxn2urGxoZVu1s3B0cVWOjGChoGiUkmTCwHY1MDfEw3aCglUV + Fhzg34STkWB7fFUfIhovKzJiYFe2tnCBgU9STU/V032vrmgvLTKysXChn2mEgVx1clajoWiwrm5YVEmF + glxKRkQ3Mz40MDtQTEYxLTpAPD9CPUI+OT8xLjYuKzNHSD0MDRm8unQ5ODcREhkoLCUoJSxQTVMpLjUN + EBhEQEo5NkIbHCgzNzsVFB0aGiAZGCAYFyAYGB8WFR8pKCc5OTIiISghICchICggHyYgHyYfHiYgHyYi + ICcdHCEtLjI4OzsYGiFWVUUiICsSFBopMSwoJy1VWV08QUQpLTE4Nj07O0E3PD4+REc9QkY9Q0YtLzEl + JyoxNjc6P0M6PkI5PD8zODorLzAoLS4rMDI1OjwyNjg1OTsmKSoqLC8oKywzODk1O0ItLDIgICgXGhsw + OTJFTEpkaXFBSUgfIyMICQgKDAopLjBFTkttc3xJUVEJDAkICwgLDgk2Pz4+REJKUVQrLiwVGBQNEQwn + LC02PjosNzg4PzsHCgYYGRsPERMkKCotMTAvNjAeIxoJDAcdJRVITExBR0g0Oj48Pj88PT0+Pz9AQkFR + V1hOVVU2PD9DREQ9Pj5TVVU8QT1UV1JCRkRFSElYWllJS0tJSko6REK3s7IxNzMyNTYqLS8vMjUfICI5 + Oz0uMiomKSQbHBwnLSZYWlxCRUgsMDRISU4iIiEuLS5JSkpbXl5DSEk8QkpTUVQQDw5CQUJQUlBjZmhG + TExGR0o4NzYgISJAQEI0OztxdnY1PDwtLjEKCQkdHiAjJSkuMTQ4PTsxNDMbGhsQFwdaYFhbZWJKVVM4 + Ozg3ODY0NDNLT0lxeHNXYl5NWVc0MzE5OTg3NjRtc21jbGZbZGFGS0cuLi0uLiwyNTRWYFteaWNXYlwc + HBwVFhcOEBEsMzRES0lJUkw/RT4UFBUuNDA9Pzo3OjomKiskJSEkJyMoKic8PDhLTU0xNTcpLC8qKyUj + JiEnKCJPUUs+QUA1OTosLSsiJSMnKScrLCo2ODg3OTo3OToWFxcQEhIODxAUFhYgICElKCchJCALDQw0 + NzdCRUI5Pj8sMjVNTEwdIy4iJy5PUE9KTk42PD41Oz1KTE0ZIjEsLjBUV1Y+Q0M7QUNAQkQ3PEEVHCQx + MzQ5Pj46P0A7Pj41NzkHCw8VGh0nKS0hJCYkKCcjJiQRERE2OTwuMjExNTYsMTM1OkEiMUEpOkpCQkFN + T1YzNzg3Oz5BU2ATHi46Rk9BRUQ0Ojs2Ojw3ODguQ1YfNEcsLTA2OzszOTk0ODkiJSkSITIhNkojJCYf + IiUgJSQeIiAYJzUzOUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + + \ No newline at end of file diff --git a/SourceGen/AppSettings.cs b/SourceGen/AppSettings.cs new file mode 100644 index 0000000..78ab6cc --- /dev/null +++ b/SourceGen/AppSettings.cs @@ -0,0 +1,320 @@ +/* + * Copyright 2018 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.Diagnostics; +using System.Text; +using System.Web.Script.Serialization; + +namespace SourceGen { + /// + /// Application settings registry. This holds both user-accessible settings and saved + /// values like window widths. + /// + /// Everything is stored as name/value pairs, where the value is serialized as a string. + /// Names are case-sensitive. + /// + /// We don't discard things we don't recognize. If we somehow end up reading a config + /// file from a newer version of the app, the various settings will be retained. + /// + public class AppSettings { + #region Names + + // Name constants. Having them defined here avoids collisions and misspellings, and + // makes it easy to find all uses. + + // Main window. + public const string MAIN_WINDOW_WIDTH = "main-window-width"; + public const string MAIN_WINDOW_HEIGHT = "main-window-height"; + public const string MAIN_LEFT_SPLITTER_DIST = "main-left-splitter-dist"; + public const string MAIN_RIGHT_SPLITTER_DIST = "main-right-splitter-dist"; + public const string MAIN_LEFT_SIDE_SPLITTER_DIST = "main-left-side-splitter-dist"; + public const string MAIN_RIGHT_SIDE_SPLITTER_DIST = "main-right-side-splitter-dist"; + + // New project dialog. + public const string NEWP_SELECTED_SYSTEM = "newp-selected-system"; + + // Formatting choices. + public const string FMT_UPPER_HEX_DIGITS = "fmt-upper-hex-digits"; + public const string FMT_UPPER_OP_MNEMONIC = "fmt-upper-op-mnemonic"; + public const string FMT_UPPER_PSEUDO_OP_MNEMONIC = "fmt-upper-pseudo-op-mnemonic"; + public const string FMT_UPPER_OPERAND_A = "fmt-upper-operand-a"; + public const string FMT_UPPER_OPERAND_S = "fmt-upper-operand-s"; + public const string FMT_UPPER_OPERAND_XY = "fmt-upper-operand-xy"; + public const string FMT_ADD_SPACE_FULL_COMMENT = "fmt-add-space-full-comment"; + + public const string FMT_OPCODE_SUFFIX_ABS = "fmt-opcode-suffix-abs"; + public const string FMT_OPCODE_SUFFIX_LONG = "fmt-opcode-suffix-long"; + public const string FMT_OPERAND_PREFIX_ABS = "fmt-operand-prefix-abs"; + public const string FMT_OPERAND_PREFIX_LONG = "fmt-operand-prefix-long"; + public const string FMT_EXPRESSION_MODE = "fmt-expression-mode"; + + public const string FMT_PSEUDO_OP_NAMES = "fmt-pseudo-op-names"; + + public const string CLIP_LINE_FORMAT = "clip-line-format"; + + // Symbol-list window options. + public const string SYMWIN_SHOW_USER = "symwin-show-user"; + public const string SYMWIN_SHOW_AUTO = "symwin-show-auto"; + public const string SYMWIN_SHOW_PROJECT = "symwin-show-project"; + public const string SYMWIN_SHOW_PLATFORM = "symwin-show-platform"; + public const string SYMWIN_SHOW_CONST = "symwin-show-const"; + public const string SYMWIN_SHOW_ADDR = "symwin-show-addr"; + public const string SYMWIN_SORT_ASCENDING = "symwin-sort-ascending"; + public const string SYMWIN_SORT_COL = "symwin-sort-col"; + + public const string SYMWIN_COL_WIDTHS = "symwin-col-widths"; + + // References window options. + public const string REFWIN_COL_WIDTHS = "refwin-col-widths"; + + // Notes window options. + public const string NOTEWIN_COL_WIDTHS = "notewin-col-widths"; + + // Code List View settings. + public const string CDLV_COL_WIDTHS = "cdlv-col-widths"; + public const string CDLV_FONT = "cdlv-font"; + + // Hex dump viewer settings. + public const string HEXD_ASCII_ONLY = "hexd-ascii-only"; + public const string HEXD_CHAR_CONV = "hexd-char-conv"; + + // ASCII chart viewer settings. + public const string ASCCH_MODE = "ascch-mode"; + + // Source generation settings. + public const string SRCGEN_DEFAULT_ASM = "srcgen-default-asm"; + public const string SRCGEN_ADD_IDENT_COMMENT = "srcgen-add-ident-comment"; + public const string SRCGEN_DISABLE_LABEL_LOCALIZATION = "srcgen-disable-label-localization"; + public const string SRCGEN_LONG_LABEL_NEW_LINE = "srcgen-long-label-new-line"; + public const string SRCGEN_SHOW_CYCLE_COUNTS = "srcgen-show-cycle-counts"; + + // Main project view settings. + public const string PRVW_RECENT_PROJECT_LIST = "prvw-recent-project-list"; + + // cc65 assembler settings + public const string ASM_CC65_EXECUTABLE = "asm-cc65-executable"; + + // Merlin 32 assembler settings. + public const string ASM_MERLIN32_EXECUTABLE = "asm-merlin32-executable"; + + // Internal debugging features. + public const string DEBUG_MENU_ENABLED = "debug-menu-enabled"; + + #endregion Names + + #region Implementation + + // App settings file header. + public const string MAGIC = "### 6502bench SourceGen settings v1.0 ###"; + + + /// + /// Single global instance of app settings. + /// + public static AppSettings Global { + get { + return sSingleton; + } + } + private static AppSettings sSingleton = new AppSettings(); + + + /// + /// Dirty flag, set to true by every "set" call. + /// + public bool Dirty { get; set; } + + /// + /// Settings storage. + /// + private Dictionary mSettings = new Dictionary(); + + + private AppSettings() { } + + /// + /// Creates a copy of this object. + /// + /// + public AppSettings GetCopy() { + AppSettings copy = new AppSettings(); + //copy.mSettings.EnsureCapacity(mSettings.Count); + foreach (KeyValuePair kvp in mSettings) { + copy.mSettings.Add(kvp.Key, kvp.Value); + } + return copy; + } + + /// + /// Replaces the existing list of settings with a new list. + /// + /// This can be used to replace the contents of the global settings object without + /// discarding the object itself, which is useful in case something has cached a + /// reference to the singleton. + /// + /// + public void ReplaceSettings(AppSettings newSettings) { + // Clone the new list, and stuff it into the old object. This way the + // objects aren't sharing lists. + mSettings = newSettings.GetCopy().mSettings; + Dirty = true; + } + + /// + /// Merges settings from another settings object into this one. + /// + /// + /// + public void MergeSettings(AppSettings newSettings) { + foreach (KeyValuePair kvp in newSettings.mSettings) { + mSettings[kvp.Key] = kvp.Value; + } + Dirty = true; + } + + /// + /// Retrieves an integer setting. + /// + /// Setting name. + /// Setting default value. + /// The value found, or the default value if no setting with the specified + /// name exists, or the stored value is not an integer. + public int GetInt(string name, int defaultValue) { + if (!mSettings.TryGetValue(name, out string valueStr)) { + return defaultValue; + } + if (!int.TryParse(valueStr, out int value)) { + Debug.WriteLine("Warning: int parse failed on " + name + "=" + valueStr); + return defaultValue; + } + return value; + } + + /// + /// Sets an integer setting. + /// + /// Setting name. + /// Setting value. + public void SetInt(string name, int value) { + mSettings[name] = value.ToString(); + Dirty = true; + } + + /// + /// Retrieves a boolean setting. + /// + /// Setting name. + /// Setting default value. + /// The value found, or the default value if no setting with the specified + /// name exists, or the stored value is not a boolean. + public bool GetBool(string name, bool defaultValue) { + if (!mSettings.TryGetValue(name, out string valueStr)) { + return defaultValue; + } + if (!bool.TryParse(valueStr, out bool value)) { + Debug.WriteLine("Warning: bool parse failed on " + name + "=" + valueStr); + return defaultValue; + } + return value; + } + + /// + /// Sets a boolean setting. + /// + /// Setting name. + /// Setting value. + public void SetBool(string name, bool value) { + mSettings[name] = value.ToString(); + Dirty = true; + } + + /// + /// Retrieves a string setting. The default value will be returned if the key + /// is not found, or if the value is null. + /// + /// Setting name. + /// Setting default value. + /// The value found, or defaultValue if not value is found. + public string GetString(string name, string defaultValue) { + if (!mSettings.TryGetValue(name, out string valueStr) || valueStr == null) { + return defaultValue; + } + return valueStr; + } + + /// + /// Sets a string setting. + /// + /// Setting name. + /// Setting value. + public void SetString(string name, string value) { + if (value == null) { + mSettings.Remove(name); + } else { + mSettings[name] = value; + } + Dirty = true; + } + + /// + /// Serializes settings dictionary into a string, for saving settings to a file. + /// + /// Serialized settings. + public string Serialize() { + StringBuilder sb = new StringBuilder(1024); + sb.Append(MAGIC); // augment with version string, which will be stripped + sb.Append("\r\n"); // will be ignored by deserializer; might get converted to \n + + JavaScriptSerializer ser = new JavaScriptSerializer(); + string cereal = ser.Serialize(mSettings); + + // add some linefeeds to make it easier for humans + cereal = CommonUtil.TextUtil.NonQuoteReplace(cereal, ",\"", ",\r\n\""); + sb.Append(cereal); + + // Stick a linefeed at the end. + sb.Append("\r\n"); + return sb.ToString(); + } + + /// + /// Deserializes settings from a string, for loading settings from a file. + /// + /// Serialized settings. + /// Deserialized settings, or null if deserialization failed. + public static AppSettings Deserialize(string cereal) { + if (!cereal.StartsWith(MAGIC)) { + return null; + } + + // Skip past header. + cereal = cereal.Substring(MAGIC.Length); + + AppSettings settings = new AppSettings(); + JavaScriptSerializer ser = new JavaScriptSerializer(); + try { + settings.mSettings = ser.Deserialize>(cereal); + return settings; + } catch (Exception ex) { + Debug.WriteLine("Settings deserialization failed: " + ex.Message); + return null; + } + } + + #endregion Implementation + } +} diff --git a/SourceGen/AsmGen/AsmCc65.cs b/SourceGen/AsmGen/AsmCc65.cs new file mode 100644 index 0000000..e2f78ca --- /dev/null +++ b/SourceGen/AsmGen/AsmCc65.cs @@ -0,0 +1,121 @@ +/* + * Copyright 2018 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 System.Diagnostics; + +using CommonUtil; + +namespace SourceGen.AsmGen { + public class AsmCc65 : IAssembler { + private List PathNames { get; set; } + + private string WorkDirectory { get; set; } + + + // IAssembler + public AssemblerVersion QueryVersion() { + string exe = AppSettings.Global.GetString(AppSettings.ASM_CC65_EXECUTABLE, null); + if (string.IsNullOrEmpty(exe)) { + return null; + } + + ShellCommand cmd = new ShellCommand(exe, "--version", + System.IO.Directory.GetCurrentDirectory(), null); + cmd.Execute(); + if (string.IsNullOrEmpty(cmd.Stdout)) { + return null; + } + + // Windows - Stderr: "cl65.exe V2.17\r\n" + // Linux - Stderr: "cl65 V2.17 - Git N/A\n" + // Other platforms may not have the ".exe". Find first occurrence of " V". + + const string PREFIX = " V"; + string str = cmd.Stderr; + int start = str.IndexOf(PREFIX); + int end = (start < 0) ? -1 : str.IndexOfAny(new char[] { ' ', '\r', '\n' }, start + 1); + + if (start < 0 || end < 0 || start + PREFIX.Length >= end) { + Debug.WriteLine("Couldn't find version in " + str); + return null; + } + start += PREFIX.Length; + string versionStr = str.Substring(start, end - start); + CommonUtil.Version version = CommonUtil.Version.Parse(versionStr); + if (!version.IsValid) { + return null; + } + return new AssemblerVersion(versionStr, version); + } + + // IAssembler + public void Configure(List pathNames, string workDirectory) { + // Clone pathNames, in case the caller decides to modify the original. + PathNames = new List(pathNames.Count); + foreach (string str in pathNames) { + PathNames.Add(str); + } + + WorkDirectory = workDirectory; + } + + // IAssembler + public AssemblerResults RunAssembler(BackgroundWorker worker) { + // Reduce input file to a partial path if possible. This is really just to make + // what we display to the user a little easier to read. + string pathName = PathNames[0]; + if (pathName.StartsWith(WorkDirectory)) { + pathName = pathName.Remove(0, WorkDirectory.Length + 1); + } else { + // Unexpected, but shouldn't be a problem. + Debug.WriteLine("NOTE: source file is not in work directory"); + } + + //string home = AppSettings.Global.GetString(AppSettings.ASM_CC65_HOME, null); + //string cl65 = AppSettings.Global.GetString(AppSettings.ASM_CC65_EXECUTABLE, null); + //Debug.Assert(!string.IsNullOrEmpty(cl65)); + //Debug.Assert(!string.IsNullOrEmpty(home)); + + //Dictionary envVars = new Dictionary(1); + //envVars.Add("CC65_HOME", home); + + //string exePath = Path.Combine(home, cl65); + + string cl65 = AppSettings.Global.GetString(AppSettings.ASM_CC65_EXECUTABLE, null); + if (string.IsNullOrEmpty(cl65)) { + Debug.WriteLine("Assembler not configured"); + return null; + } + + // Wrap pathname in quotes in case it has spaces. + // (Do we need to shell-escape quotes in the pathName?) + ShellCommand cmd = new ShellCommand(cl65, + "--target none \"" + pathName + "\"", WorkDirectory, null); + cmd.Execute(); + + // Can't really do anything with a "cancel" request. + + // Output filename is the input filename without the ".S". Since the filename + // was generated by us we can be confident in the format. + string outputFile = PathNames[0].Substring(0, PathNames[0].Length - 2); + + return new AssemblerResults(cmd.FullCommandLine, cmd.ExitCode, cmd.Stdout, + cmd.Stderr, outputFile); + } + } +} diff --git a/SourceGen/AsmGen/AsmMerlin32.cs b/SourceGen/AsmGen/AsmMerlin32.cs new file mode 100644 index 0000000..61f187e --- /dev/null +++ b/SourceGen/AsmGen/AsmMerlin32.cs @@ -0,0 +1,110 @@ +/* + * Copyright 2018 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 System.Diagnostics; + +using CommonUtil; + +namespace SourceGen.AsmGen { + public class AsmMerlin32 : IAssembler { + private List PathNames { get; set; } + + private string WorkDirectory { get; set; } + + + // IAssembler + public AssemblerVersion QueryVersion() { + string exe = AppSettings.Global.GetString(AppSettings.ASM_MERLIN32_EXECUTABLE, null); + if (string.IsNullOrEmpty(exe)) { + return null; + } + + ShellCommand cmd = new ShellCommand(exe, string.Empty, + System.IO.Directory.GetCurrentDirectory(), null); + cmd.Execute(); + if (string.IsNullOrEmpty(cmd.Stdout)) { + return null; + } + + // Stdout: "C:\Src\WorkBench\Merlin32.exe v 1.0, (c) Brutal Deluxe ..." + // Other platforms may not have the ".exe". Find first occurrence of " v ". + + const string PREFIX = " v "; // not expecting this to appear in the path + string str = cmd.Stdout; + int start = str.IndexOf(PREFIX); + int end = (start < 0) ? -1 : str.IndexOf(',', start); + + if (start < 0 || end < 0 || start + PREFIX.Length >= end) { + Debug.WriteLine("Couldn't find version in " + str); + return null; + } + start += PREFIX.Length; + string versionStr = str.Substring(start, end - start); + CommonUtil.Version version = CommonUtil.Version.Parse(versionStr); + if (!version.IsValid) { + return null; + } + return new AssemblerVersion(versionStr, version); + } + + // IAssembler + public void Configure(List pathNames, string workDirectory) { + // Clone pathNames, in case the caller decides to modify the original. + PathNames = new List(pathNames.Count); + foreach (string str in pathNames) { + PathNames.Add(str); + } + + WorkDirectory = workDirectory; + } + + // IAssembler + public AssemblerResults RunAssembler(BackgroundWorker worker) { + // Reduce input file to a partial path if possible. This is really just to make + // what we display to the user a little easier to read. + string pathName = PathNames[0]; + if (pathName.StartsWith(WorkDirectory)) { + pathName = pathName.Remove(0, WorkDirectory.Length + 1); + } else { + // Unexpected, but shouldn't be a problem. + Debug.WriteLine("NOTE: source file is not in work directory"); + } + + string exe = AppSettings.Global.GetString(AppSettings.ASM_MERLIN32_EXECUTABLE, null); + if (string.IsNullOrEmpty(exe)) { + Debug.WriteLine("Assembler not configured"); + return null; + } + + // Wrap pathname in quotes in case it has spaces. + // (Do we need to shell-escape quotes in the pathName?) + ShellCommand cmd = new ShellCommand(exe, ". \"" + pathName + "\"", + WorkDirectory, null); + cmd.Execute(); + + // Can't really do anything with a "cancel" request. + + // Output filename is the input filename without the ".S". Since the filename + // was generated by us we can be confident in the format. + string outputFile = PathNames[0].Substring(0, PathNames[0].Length - 2); + + return new AssemblerResults(cmd.FullCommandLine, cmd.ExitCode, cmd.Stdout, + cmd.Stderr, outputFile); + } + } +} diff --git a/SourceGen/AsmGen/AssemblerInfo.cs b/SourceGen/AsmGen/AssemblerInfo.cs new file mode 100644 index 0000000..5b1c176 --- /dev/null +++ b/SourceGen/AsmGen/AssemblerInfo.cs @@ -0,0 +1,154 @@ +/* + * Copyright 2018 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; +using System.Collections.Generic; + +namespace SourceGen.AsmGen { + /// + /// Static information on assemblers supported by SourceGen. This is relevant for both + /// assembly source generation and assembler execution. Nothing here is affected + /// by whether or not the assembler in question is actually installed. + /// + public class AssemblerInfo { + /// + /// Enumeration of supported assemblers. Alphabetical order looks nicest. + /// + public enum Id { + Unknown = 0, + Cc65, + Merlin32 + } + + /// + /// Identifier. + /// + public Id AssemblerId { get; private set; } + + /// + /// Human-readable name. + /// + public string Name { get; private set; } + + + private AssemblerInfo(Id id, string name) { + AssemblerId = id; + Name = name; + } + + // For simplicity, this is 1:1 with the Id enum. + private static AssemblerInfo[] sInfo = new AssemblerInfo[] { + new AssemblerInfo(Id.Unknown, "???"), + new AssemblerInfo(Id.Cc65, "cc65"), + new AssemblerInfo(Id.Merlin32, "Merlin 32") + }; + + /// + /// Returns an AssemblerInfo object for the specified id. + /// + /// Assembler identifier. + /// Reference to AssemblerInfo object. + public static AssemblerInfo GetAssemblerInfo(Id id) { + return sInfo[(int)id]; + } + + /// + /// Generator factory method. + /// + /// ID of assembler to return generator object for. + /// New source generator object. + public static IGenerator GetGenerator(AssemblerInfo.Id id) { + switch (id) { + case Id.Cc65: + return new GenCc65(); + case Id.Merlin32: + return new GenMerlin32(); + default: + return null; + } + } + + /// + /// Assembler factory method. + /// + /// ID of assembler to return assembler object for. + /// New assembler interface object. + public static IAssembler GetAssembler(AssemblerInfo.Id id) { + switch (id) { + case Id.Cc65: + return new AsmCc65(); + case Id.Merlin32: + return new AsmMerlin32(); + default: + return null; + } + } + + /// + /// Provides a way to iterate through the set of known assemblers. This is probably + /// YAGNI -- we could just return the array -- but it would allow us to apply filters, + /// e.g. strip out assemblers that don't support 65816 code when that's the selected + /// CPU definition. + /// + private class AssemblerInfoIterator : IEnumerator { + private int mIndex = -1; + + public AssemblerInfo Current { + get { + if (mIndex < 0) { + // not started + return null; + } + return sInfo[mIndex]; + } + } + + object IEnumerator.Current { + get { + return Current; + } + } + + public void Dispose() { } + + public bool MoveNext() { + if (mIndex < 0) { + // skip element 0 (Unknown) + mIndex = 1; + } else { + mIndex++; + if (mIndex >= sInfo.Length) { + return false; + } + } + return true; + } + + public void Reset() { + mIndex = -1; + } + } + + public static IEnumerator GetInfoEnumerator() { + return new AssemblerInfoIterator(); + } + + + public override string ToString() { + return "Asm " + ((int)AssemblerId).ToString() + ": " + Name; + } + } +} diff --git a/SourceGen/AsmGen/AssemblerProgress.Designer.cs b/SourceGen/AsmGen/AssemblerProgress.Designer.cs new file mode 100644 index 0000000..7d2c523 --- /dev/null +++ b/SourceGen/AsmGen/AssemblerProgress.Designer.cs @@ -0,0 +1,113 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AsmGen { + partial class AssemblerProgress { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.progressLabel = new System.Windows.Forms.Label(); + this.progressBar1 = new System.Windows.Forms.ProgressBar(); + this.cancelButton = new System.Windows.Forms.Button(); + this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker(); + this.SuspendLayout(); + // + // progressLabel + // + this.progressLabel.AutoSize = true; + this.progressLabel.Location = new System.Drawing.Point(13, 13); + this.progressLabel.Name = "progressLabel"; + this.progressLabel.Size = new System.Drawing.Size(61, 13); + this.progressLabel.TabIndex = 1; + this.progressLabel.Text = "Preparing..."; + // + // progressBar1 + // + this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.progressBar1.Location = new System.Drawing.Point(12, 39); + this.progressBar1.Name = "progressBar1"; + this.progressBar1.Size = new System.Drawing.Size(600, 23); + this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee; + this.progressBar1.TabIndex = 2; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.cancelButton.Location = new System.Drawing.Point(275, 79); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 0; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // backgroundWorker1 + // + this.backgroundWorker1.WorkerReportsProgress = true; + this.backgroundWorker1.WorkerSupportsCancellation = true; + this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork); + this.backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker1_ProgressChanged); + this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted); + // + // AssemblerProgress + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(624, 114); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.progressBar1); + this.Controls.Add(this.progressLabel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AssemblerProgress"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Assembling Source..."; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.AssemblerProgress_FormClosing); + this.Load += new System.EventHandler(this.AssemblerProgress_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label progressLabel; + private System.Windows.Forms.ProgressBar progressBar1; + private System.Windows.Forms.Button cancelButton; + private System.ComponentModel.BackgroundWorker backgroundWorker1; + } +} \ No newline at end of file diff --git a/SourceGen/AsmGen/AssemblerProgress.cs b/SourceGen/AsmGen/AssemblerProgress.cs new file mode 100644 index 0000000..62a1d85 --- /dev/null +++ b/SourceGen/AsmGen/AssemblerProgress.cs @@ -0,0 +1,124 @@ +/* + * Copyright 2018 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 System.Diagnostics; +using System.Windows.Forms; + +namespace SourceGen.AsmGen { + /// + /// Dialog that shows the progress of the assembler, and allows cancellation. + /// + public partial class AssemblerProgress : Form { + /// + /// On success, assembler results will be here. + /// + public AssemblerResults Results { get; private set; } + + /// + /// Assembler executor. + /// + private IAssembler mAssembler; + + + /// + /// Constructor. + /// + /// Fully-configured source generator. + public AssemblerProgress(IAssembler asm) { + InitializeComponent(); + + mAssembler = asm; + } + + private void AssemblerProgress_Load(object sender, EventArgs e) { + backgroundWorker1.RunWorkerAsync(); + } + + private void cancelButton_Click(object sender, EventArgs e) { + backgroundWorker1.CancelAsync(); + cancelButton.Enabled = false; + + // We don't have a polite way to ask a shell command to stop, so we should + // kill the process here. Need to figure out how to do that. We don't need + // to clean up partial output. + } + + private void AssemblerProgress_FormClosing(object sender, FormClosingEventArgs e) { + // Strictly speaking, we should treat this as a cancel request, and set + // e.Cancel = true to prevent the form from closing until the assembler stops. + // However, we don't currently kill runaway processes, which would leave the + // user with no way to close the dialog, potentially requiring them to kill the + // entire app with unsaved work. Better to abandon the runaway process. + // + // We call CancelAsync so that the results are discarded should the worker + // eventually finish. + if (backgroundWorker1.IsBusy) { + backgroundWorker1.CancelAsync(); + DialogResult = DialogResult.Cancel; + } + } + + // NOTE: executes on work thread. DO NOT do any UI work here. DO NOT access + // the Results property directly. + private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { + BackgroundWorker worker = sender as BackgroundWorker; + + AssemblerResults results = mAssembler.RunAssembler(worker); + if (worker.CancellationPending) { + e.Cancel = true; + } else { + e.Result = results; + } + } + + // Callback that fires when a progress update is made. + private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { + int percent = e.ProgressPercentage; + string msg = e.UserState as string; + + Debug.Assert(percent >= 0 && percent <= 100); + + if (!string.IsNullOrEmpty(msg)) { + progressLabel.Text = msg; + } + progressBar1.Value = percent; + } + + // Callback that fires when execution completes. + private void backgroundWorker1_RunWorkerCompleted(object sender, + RunWorkerCompletedEventArgs e) { + if (e.Cancelled) { + Debug.WriteLine("CANCELED"); + DialogResult = DialogResult.Cancel; + } else if (e.Error != null) { + // Unexpected -- shell command execution shouldn't throw exceptions. + MessageBox.Show(e.Error.ToString(), Properties.Resources.OPERATION_FAILED, + MessageBoxButtons.OK, MessageBoxIcon.Error); + DialogResult = DialogResult.Cancel; + } else { + // Make results available in properties. + Results = e.Result as AssemblerResults; + Debug.WriteLine("Asm complete, exit=" + Results.ExitCode); + DialogResult = DialogResult.OK; + } + + // Whatever the case, we're done. + this.Close(); + } + } +} diff --git a/SourceGen/AsmGen/AssemblerProgress.resx b/SourceGen/AsmGen/AssemblerProgress.resx new file mode 100644 index 0000000..59099f2 --- /dev/null +++ b/SourceGen/AsmGen/AssemblerProgress.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/SourceGen/AsmGen/AssemblerVersion.cs b/SourceGen/AsmGen/AssemblerVersion.cs new file mode 100644 index 0000000..f52a1ae --- /dev/null +++ b/SourceGen/AsmGen/AssemblerVersion.cs @@ -0,0 +1,104 @@ +/* + * Copyright 2018 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.Diagnostics; + +namespace SourceGen.AsmGen { + public class AssemblerVersion { + /// + /// Version string reported by the assembler. + /// + public string VersionStr { get; private set; } + + /// + /// Version string converted to a Version object. For very complex version strings, + /// some information may be lost in the conversion. + /// + public CommonUtil.Version Version { get; private set; } + + //// Command pathname and modification date. Useful for caching values. + //private string ExeName { get; set; } + //private DateTime ExeModWhen { get; set; } + + public AssemblerVersion(string versionStr, CommonUtil.Version version) { + VersionStr = versionStr; + Version = version; + } + + public static AssemblerVersion GetVersion(AssemblerInfo.Id id) { + IAssembler asm = AssemblerInfo.GetAssembler(id); + if (asm == null) { + Debug.WriteLine("Assembler " + id + " not configured"); + return null; + } + return asm.QueryVersion(); + } + + public override string ToString() { + return "[" + VersionStr + "/" + Version + "]"; + } + } + + /// + /// Maintains a cache of the versions of installed assemblers. + /// + public static class AssemblerVersionCache { + private static Dictionary sVersions = + new Dictionary(); + private static bool sQueried = false; + + /// + /// Queries the versions from all known assemblers, replacing any previously held data. + /// + public static void QueryVersions() { + IEnumerator iter = AssemblerInfo.GetInfoEnumerator(); + while (iter.MoveNext()) { + AssemblerInfo.Id id = iter.Current.AssemblerId; + if (id == AssemblerInfo.Id.Unknown) { + continue; + } + + AssemblerVersion vers = null; + IAssembler asm = AssemblerInfo.GetAssembler(id); + if (asm != null) { + vers = asm.QueryVersion(); + } + + Debug.WriteLine("Asm version query: " + id + "=" + vers); + sVersions[id] = vers; + } + + sQueried = true; + } + + /// + /// Returns the version information, or null if the query failed for this assembler. + /// + /// Assembler identifier. + /// Version info. + public static AssemblerVersion GetVersion(AssemblerInfo.Id id) { + if (!sQueried) { + QueryVersions(); + } + if (sVersions.TryGetValue(id, out AssemblerVersion vers)) { + return vers; + } else { + return null; + } + } + } +} diff --git a/SourceGen/AsmGen/GenAndAsm.Designer.cs b/SourceGen/AsmGen/GenAndAsm.Designer.cs new file mode 100644 index 0000000..2110dc7 --- /dev/null +++ b/SourceGen/AsmGen/GenAndAsm.Designer.cs @@ -0,0 +1,298 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AsmGen { + partial class GenAndAsm { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.assemblerSettingsButton = new System.Windows.Forms.Button(); + this.previewFileComboBox = new System.Windows.Forms.ComboBox(); + this.previewFileLabel = new System.Windows.Forms.Label(); + this.workDirectoryTextBox = new System.Windows.Forms.TextBox(); + this.outputFileLabel = new System.Windows.Forms.Label(); + this.generateButton = new System.Windows.Forms.Button(); + this.previewTextBox = new System.Windows.Forms.TextBox(); + this.assemblerComboBox = new System.Windows.Forms.ComboBox(); + this.assemblerLabel = new System.Windows.Forms.Label(); + this.configureAsmLinkLabel = new System.Windows.Forms.LinkLabel(); + this.cmdOutputTextBox = new System.Windows.Forms.TextBox(); + this.runAssemblerButton = new System.Windows.Forms.Button(); + this.basePanel = new System.Windows.Forms.Panel(); + this.closeButton = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.basePanel.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.assemblerSettingsButton); + this.splitContainer1.Panel1.Controls.Add(this.previewFileComboBox); + this.splitContainer1.Panel1.Controls.Add(this.previewFileLabel); + this.splitContainer1.Panel1.Controls.Add(this.workDirectoryTextBox); + this.splitContainer1.Panel1.Controls.Add(this.outputFileLabel); + this.splitContainer1.Panel1.Controls.Add(this.generateButton); + this.splitContainer1.Panel1.Controls.Add(this.previewTextBox); + this.splitContainer1.Panel1.Controls.Add(this.assemblerComboBox); + this.splitContainer1.Panel1.Controls.Add(this.assemblerLabel); + this.splitContainer1.Panel1MinSize = 150; + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.configureAsmLinkLabel); + this.splitContainer1.Panel2.Controls.Add(this.cmdOutputTextBox); + this.splitContainer1.Panel2.Controls.Add(this.runAssemblerButton); + this.splitContainer1.Panel2MinSize = 100; + this.splitContainer1.Size = new System.Drawing.Size(784, 612); + this.splitContainer1.SplitterDistance = 400; + this.splitContainer1.TabIndex = 0; + // + // assemblerSettingsButton + // + this.assemblerSettingsButton.Location = new System.Drawing.Point(241, 10); + this.assemblerSettingsButton.Name = "assemblerSettingsButton"; + this.assemblerSettingsButton.Size = new System.Drawing.Size(75, 23); + this.assemblerSettingsButton.TabIndex = 3; + this.assemblerSettingsButton.Text = "Settings"; + this.assemblerSettingsButton.UseVisualStyleBackColor = true; + this.assemblerSettingsButton.Click += new System.EventHandler(this.assemblerSettingsButton_Click); + // + // previewFileComboBox + // + this.previewFileComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.previewFileComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.previewFileComboBox.FormattingEnabled = true; + this.previewFileComboBox.Items.AddRange(new object[] { + "SampleFile#031234_Merlin32.S"}); + this.previewFileComboBox.Location = new System.Drawing.Point(83, 58); + this.previewFileComboBox.Name = "previewFileComboBox"; + this.previewFileComboBox.Size = new System.Drawing.Size(262, 21); + this.previewFileComboBox.TabIndex = 5; + this.previewFileComboBox.SelectedIndexChanged += new System.EventHandler(this.previewFileComboBox_SelectedIndexChanged); + // + // previewFileLabel + // + this.previewFileLabel.AutoSize = true; + this.previewFileLabel.Location = new System.Drawing.Point(13, 61); + this.previewFileLabel.Name = "previewFileLabel"; + this.previewFileLabel.Size = new System.Drawing.Size(64, 13); + this.previewFileLabel.TabIndex = 4; + this.previewFileLabel.Text = "Preview file:"; + // + // workDirectoryTextBox + // + this.workDirectoryTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.workDirectoryTextBox.Location = new System.Drawing.Point(445, 59); + this.workDirectoryTextBox.Name = "workDirectoryTextBox"; + this.workDirectoryTextBox.ReadOnly = true; + this.workDirectoryTextBox.Size = new System.Drawing.Size(327, 20); + this.workDirectoryTextBox.TabIndex = 7; + this.workDirectoryTextBox.Text = "C:\\this\\that\\theother"; + // + // outputFileLabel + // + this.outputFileLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.outputFileLabel.AutoSize = true; + this.outputFileLabel.Location = new System.Drawing.Point(360, 62); + this.outputFileLabel.Name = "outputFileLabel"; + this.outputFileLabel.Size = new System.Drawing.Size(79, 13); + this.outputFileLabel.TabIndex = 6; + this.outputFileLabel.Text = "Work directory:"; + // + // generateButton + // + this.generateButton.Location = new System.Drawing.Point(363, 10); + this.generateButton.Name = "generateButton"; + this.generateButton.Size = new System.Drawing.Size(94, 23); + this.generateButton.TabIndex = 0; + this.generateButton.Text = "Generate"; + this.generateButton.UseVisualStyleBackColor = true; + this.generateButton.Click += new System.EventHandler(this.generateButton_Click); + // + // previewTextBox + // + this.previewTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.previewTextBox.BackColor = System.Drawing.SystemColors.Window; + this.previewTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.previewTextBox.Location = new System.Drawing.Point(13, 85); + this.previewTextBox.MaxLength = 0; + this.previewTextBox.Multiline = true; + this.previewTextBox.Name = "previewTextBox"; + this.previewTextBox.ReadOnly = true; + this.previewTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.previewTextBox.Size = new System.Drawing.Size(759, 303); + this.previewTextBox.TabIndex = 8; + // + // assemblerComboBox + // + this.assemblerComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.assemblerComboBox.FormattingEnabled = true; + this.assemblerComboBox.Items.AddRange(new object[] { + "Merlin32", + "CA65"}); + this.assemblerComboBox.Location = new System.Drawing.Point(83, 12); + this.assemblerComboBox.Name = "assemblerComboBox"; + this.assemblerComboBox.Size = new System.Drawing.Size(152, 21); + this.assemblerComboBox.TabIndex = 2; + this.assemblerComboBox.SelectedIndexChanged += new System.EventHandler(this.assemblerComboBox_SelectedIndexChanged); + // + // assemblerLabel + // + this.assemblerLabel.AutoSize = true; + this.assemblerLabel.Location = new System.Drawing.Point(13, 15); + this.assemblerLabel.Name = "assemblerLabel"; + this.assemblerLabel.Size = new System.Drawing.Size(58, 13); + this.assemblerLabel.TabIndex = 1; + this.assemblerLabel.Text = "Assembler:"; + // + // configureAsmLinkLabel + // + this.configureAsmLinkLabel.AutoSize = true; + this.configureAsmLinkLabel.Location = new System.Drawing.Point(124, 11); + this.configureAsmLinkLabel.Name = "configureAsmLinkLabel"; + this.configureAsmLinkLabel.Size = new System.Drawing.Size(126, 13); + this.configureAsmLinkLabel.TabIndex = 1; + this.configureAsmLinkLabel.TabStop = true; + this.configureAsmLinkLabel.Text = "Assembler not configured"; + this.configureAsmLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.configureAsmLinkLabel_LinkClicked); + // + // cmdOutputTextBox + // + this.cmdOutputTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.cmdOutputTextBox.BackColor = System.Drawing.SystemColors.Window; + this.cmdOutputTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cmdOutputTextBox.Location = new System.Drawing.Point(10, 36); + this.cmdOutputTextBox.MaxLength = 0; + this.cmdOutputTextBox.Multiline = true; + this.cmdOutputTextBox.Name = "cmdOutputTextBox"; + this.cmdOutputTextBox.ReadOnly = true; + this.cmdOutputTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.cmdOutputTextBox.Size = new System.Drawing.Size(762, 169); + this.cmdOutputTextBox.TabIndex = 2; + // + // runAssemblerButton + // + this.runAssemblerButton.Location = new System.Drawing.Point(10, 6); + this.runAssemblerButton.Name = "runAssemblerButton"; + this.runAssemblerButton.Size = new System.Drawing.Size(97, 23); + this.runAssemblerButton.TabIndex = 0; + this.runAssemblerButton.Text = "Run Assembler"; + this.runAssemblerButton.UseVisualStyleBackColor = true; + this.runAssemblerButton.Click += new System.EventHandler(this.runAssemblerButton_Click); + // + // basePanel + // + this.basePanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.basePanel.Controls.Add(this.splitContainer1); + this.basePanel.Location = new System.Drawing.Point(0, 0); + this.basePanel.Name = "basePanel"; + this.basePanel.Size = new System.Drawing.Size(784, 612); + this.basePanel.TabIndex = 0; + // + // closeButton + // + this.closeButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.closeButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.closeButton.Location = new System.Drawing.Point(697, 626); + this.closeButton.Name = "closeButton"; + this.closeButton.Size = new System.Drawing.Size(75, 23); + this.closeButton.TabIndex = 0; + this.closeButton.Text = "Close"; + this.closeButton.UseVisualStyleBackColor = true; + // + // GenAndAsm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.closeButton; + this.ClientSize = new System.Drawing.Size(784, 661); + this.Controls.Add(this.closeButton); + this.Controls.Add(this.basePanel); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(800, 600); + this.Name = "GenAndAsm"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Generate and Assemble"; + this.Load += new System.EventHandler(this.GenAndAsm_Load); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + this.splitContainer1.Panel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.basePanel.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.Panel basePanel; + private System.Windows.Forms.Button closeButton; + private System.Windows.Forms.Button generateButton; + private System.Windows.Forms.TextBox previewTextBox; + private System.Windows.Forms.ComboBox assemblerComboBox; + private System.Windows.Forms.Label assemblerLabel; + private System.Windows.Forms.TextBox cmdOutputTextBox; + private System.Windows.Forms.Button runAssemblerButton; + private System.Windows.Forms.TextBox workDirectoryTextBox; + private System.Windows.Forms.Label outputFileLabel; + private System.Windows.Forms.ComboBox previewFileComboBox; + private System.Windows.Forms.Label previewFileLabel; + private System.Windows.Forms.Button assemblerSettingsButton; + private System.Windows.Forms.LinkLabel configureAsmLinkLabel; + } +} \ No newline at end of file diff --git a/SourceGen/AsmGen/GenAndAsm.cs b/SourceGen/AsmGen/GenAndAsm.cs new file mode 100644 index 0000000..28faf60 --- /dev/null +++ b/SourceGen/AsmGen/GenAndAsm.cs @@ -0,0 +1,411 @@ +/* + * Copyright 2018 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 System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Text; +using System.Windows.Forms; + +namespace SourceGen.AsmGen { + public partial class GenAndAsm : Form { + private const int PREVIEW_BUF_SIZE = 64 * 1024; // 64KB should be enough for preview + private static string NO_PREVIEW_FILES = + "<" + Properties.Resources.NO_FILES_AVAILABLE + ">"; + + /// + /// Holds data for the preview combo box. + /// + private class ComboPath { + public string FileName { get; private set; } + public string PathName { get; private set; } + public ComboPath(string pathName) { + PathName = pathName; + if (string.IsNullOrEmpty(pathName)) { + FileName = NO_PREVIEW_FILES; + } else { + FileName = Path.GetFileName(pathName); + } + } + public override string ToString() { + return FileName; + } + } + + /// + /// Project view. Needed so we can pop open the assembler settings dialog. + /// + private AppForms.ProjectView mProjView; + + /// + /// Project with data. + /// + private DisasmProject mProject; + + /// + /// Directory where generated files and assembler output will go. + /// + private string mWorkDirectory; + + /// + /// Base file name. For example, if this is "GenFile", we might generate + /// "GenFile_Cc65.S". + /// + private string mBaseFileName; + + /// + /// Currently-selected assembler ID. + /// + private AssemblerInfo.Id mSelectedAssemblerId; + + /// + /// Results from last source generation. + /// + private List mGenerationResults; + + /// + /// Holds an item for the pick-your-assembler combox box. + /// + private class AsmComboItem { + public AssemblerInfo.Id AssemblerId { get; private set; } + public string Name { get; private set; } + public AssemblerVersion AsmVersion { get; private set; } + + public AsmComboItem(AssemblerInfo info, AssemblerVersion version) { + AssemblerId = info.AssemblerId; + Name = info.Name; + AsmVersion = version; + } + // This determines what the combo box shows. + public override string ToString() { + if (AsmVersion == null) { + return Name + " " + Properties.Resources.ASM_LATEST_VERSION; + } else { + return Name + " v" + AsmVersion.VersionStr; + } + } + } + + + /// + /// Constructor. + /// + /// Full path to the project file. + public GenAndAsm(AppForms.ProjectView projectView, DisasmProject project, + string projectPathName) { + InitializeComponent(); + + mProjView = projectView; + mProject = project; + mWorkDirectory = Path.GetDirectoryName(projectPathName); + mBaseFileName = Path.GetFileNameWithoutExtension(projectPathName); + + workDirectoryTextBox.Text = mWorkDirectory; + + // Make the splitter visible. This is a little tricky because the parent color + // affects the children as well. + // https://stackoverflow.com/a/22888877/294248 + this.splitContainer1.Panel1.BackColor = SystemColors.ControlDark; + this.splitContainer1.Panel2.BackColor = SystemColors.ControlDark; + this.splitContainer1.BackColor = SystemColors.ControlDark; + this.splitContainer1.Panel1.BackColor = SystemColors.Control; + this.splitContainer1.Panel2.BackColor = SystemColors.Control; + } + + private void GenAndAsm_Load(object sender, EventArgs e) { + // Try to select the previously-used asm format. + string defaultAsm = + AppSettings.Global.GetString(AppSettings.SRCGEN_DEFAULT_ASM, string.Empty); + PopulateAssemblerComboBox(defaultAsm); + + ResetElements(); + } + + /// + /// Populates the assembler combo box. Attempts to match the defaultAsm arg with + /// the entries to configure the initial value. + /// + private void PopulateAssemblerComboBox(string defaultAsm) { + //assemblerComboBox.DisplayMember = "Name"; // show this property + + assemblerComboBox.Items.Clear(); + IEnumerator iter = AssemblerInfo.GetInfoEnumerator(); + bool foundMatch = false; + while (iter.MoveNext()) { + AssemblerInfo info = iter.Current; + AssemblerVersion version = AssemblerVersionCache.GetVersion(info.AssemblerId); + AsmComboItem item = new AsmComboItem(info, version); + assemblerComboBox.Items.Add(item); + if (item.AssemblerId.ToString() == defaultAsm) { + Debug.WriteLine("matched current " + defaultAsm); + assemblerComboBox.SelectedItem = item; + foundMatch = true; + } + } + if (!foundMatch) { + // Need to do this or box will show empty. + assemblerComboBox.SelectedIndex = 0; + } + } + + private void assemblerComboBox_SelectedIndexChanged(object sender, EventArgs e) { + AsmComboItem sel = (AsmComboItem)assemblerComboBox.SelectedItem; + if (mSelectedAssemblerId != sel.AssemblerId) { + // Selection changed, discard window contents. + mSelectedAssemblerId = sel.AssemblerId; + AppSettings.Global.SetString(AppSettings.SRCGEN_DEFAULT_ASM, + mSelectedAssemblerId.ToString()); + ResetElements(); + } + } + + private void previewFileComboBox_SelectedIndexChanged(object sender, EventArgs e) { + ComboPath cpath = (ComboPath) previewFileComboBox.SelectedItem; + if (string.IsNullOrEmpty(cpath.PathName)) { + // nothing to do + return; + } + + previewTextBox.BackColor = SystemColors.Window; + previewTextBox.Enabled = true; + LoadPreviewFile(cpath.PathName); + } + + /// + /// Resets all of the active elements to the initial state, before any source code + /// was generated. + /// + private void ResetElements() { + mGenerationResults = null; + previewFileComboBox.Items.Clear(); + previewFileComboBox.Items.Add(new ComboPath(null)); + previewFileComboBox.SelectedIndex = 0; + + previewTextBox.Text = string.Empty; + previewTextBox.BackColor = SystemColors.Control; + previewTextBox.Enabled = false; + + cmdOutputTextBox.Text = string.Empty; + cmdOutputTextBox.BackColor = SystemColors.Control; + + UpdateAssemblerControls(); + } + + /// + /// Updates the controls in the lower (assembler) half of the dialog. + /// + private void UpdateAssemblerControls() { + bool asmConf = IsAssemblerConfigured(); + Debug.WriteLine("ID=" + mSelectedAssemblerId + " asmConf=" + asmConf); + configureAsmLinkLabel.Visible = !asmConf; + if (mGenerationResults == null || !asmConf) { + runAssemblerButton.Enabled = false; + } else { + runAssemblerButton.Enabled = true; + } + } + + /// + /// Returns true if the selected assembler has been configured. + /// + private bool IsAssemblerConfigured() { + string settingStr; + switch (mSelectedAssemblerId) { + case AssemblerInfo.Id.Cc65: + settingStr = AppSettings.ASM_CC65_EXECUTABLE; + break; + case AssemblerInfo.Id.Merlin32: + settingStr = AppSettings.ASM_MERLIN32_EXECUTABLE; + break; + default: + Debug.Assert(false); + return false; + } + return !string.IsNullOrEmpty(AppSettings.Global.GetString(settingStr, null)); + } + + private void assemblerSettingsButton_Click(object sender, EventArgs e) { + DoSettings(); + } + + private void configureAsmLinkLabel_LinkClicked(object sender, + LinkLabelLinkClickedEventArgs e) { + DoSettings(); + } + + /// + /// Configures assembler-specific items, such as the installation directory of the + /// assembler binary. + /// + private void DoSettings() { + // Pop open the app settings dialog, with the appropriate tab selected. + mProjView.ShowAppSettings(AppForms.EditAppSettings.Tab.Assembler); + + // Update the controls based on whether or not the assembler is now available. + UpdateAssemblerControls(); + AsmComboItem item = (AsmComboItem)assemblerComboBox.SelectedItem; + PopulateAssemblerComboBox(item.AssemblerId.ToString()); + } + + private void generateButton_Click(object sender, EventArgs e) { + IGenerator gen = AssemblerInfo.GetGenerator(mSelectedAssemblerId); + if (gen == null) { + Debug.WriteLine("Unable to get generator for " + mSelectedAssemblerId); + return; + } + gen.Configure(mProject, mWorkDirectory, mBaseFileName, + AssemblerVersionCache.GetVersion(mSelectedAssemblerId), AppSettings.Global); + + GeneratorProgress dlg = new GeneratorProgress(gen); + dlg.ShowDialog(); + Debug.WriteLine("Dialog returned: " + dlg.DialogResult); + + List pathNames = dlg.Results; + dlg.Dispose(); + + if (pathNames == null) { + // errors already reported + return; + } + + ResetElements(); + mGenerationResults = pathNames; + previewFileComboBox.Items.Clear(); + foreach (string str in pathNames) { + previewFileComboBox.Items.Add(new ComboPath(str)); + } + previewFileComboBox.SelectedIndex = 0; // should trigger update + + UpdateAssemblerControls(); + } + + private void LoadPreviewFile(string pathName) { + Debug.WriteLine("LOAD " + pathName); + + try { + using (StreamReader sr = new StreamReader(pathName, Encoding.UTF8)) { + char[] bigbuf = new char[PREVIEW_BUF_SIZE]; + int actual = sr.Read(bigbuf, 0, bigbuf.Length); + string str = CharArrayToLineNumberedString(bigbuf); + if (actual < PREVIEW_BUF_SIZE) { + previewTextBox.Text = str; + } else { + previewTextBox.Text = str + "\r\n" + + Properties.Resources.ERR_TOO_LARGE_FOR_PREVIEW; + } + } + } catch (Exception ex) { + previewTextBox.Text = ex.ToString(); + } + } + + /// + /// Converts a char[] to a string, inserting line numbers. + /// + /// + /// + private static string CharArrayToLineNumberedString(char[] data) { + StringBuilder sb = new StringBuilder(data.Length + data.Length / 40); + int lineStart = 0; + int lineNum = 0; + + for (int i = 0; i < data.Length; i++) { + if (data[i] == '\n') { + sb.AppendFormat("{0,4:D0} ", ++lineNum); + sb.Append(data, lineStart, i - lineStart + 1); + lineStart = i + 1; + } + } + + return sb.ToString(); + } + + private void runAssemblerButton_Click(object sender, EventArgs e) { + IAssembler asm = AssemblerInfo.GetAssembler(mSelectedAssemblerId); + if (asm == null) { + Debug.WriteLine("Unable to get assembler for " + mSelectedAssemblerId); + return; + } + + asm.Configure(mGenerationResults, mWorkDirectory); + AssemblerProgress dlg = new AssemblerProgress(asm); + dlg.ShowDialog(); + Debug.WriteLine("Dialog returned: " + dlg.DialogResult); + if (dlg.DialogResult != DialogResult.OK) { + // Cancelled, or failed to even run the assembler. + return; + } + + AssemblerResults results = dlg.Results; + if (results == null) { + Debug.WriteLine("Dialog returned OK, but no assembler results found"); + Debug.Assert(false); + return; + } + + StringBuilder sb = + new StringBuilder(results.Stdout.Length + results.Stderr.Length + 200); + sb.Append(results.CommandLine); + sb.Append("\r\n"); + sb.AppendFormat("ExitCode={0} - ", results.ExitCode); + if (results.ExitCode == 0) { + FileInfo fi = new FileInfo(results.OutputPathName); + if (!fi.Exists) { + MessageBox.Show(Properties.Resources.ASM_OUTPUT_NOT_FOUND, + Properties.Resources.ASM_MISMATCH_CAPTION, + MessageBoxButtons.OK, MessageBoxIcon.Error); + sb.Append(Properties.Resources.ASM_MATCH_FAILURE); + } else if (!CommonUtil.FileUtil.CompareBinaryFile(mProject.FileData, + results.OutputPathName, out int offset, out byte fileVal)) { + if (fi.Length != mProject.FileData.Length && + offset == fi.Length || offset == mProject.FileData.Length) { + // The files matched up to the point where one ended. + string msg = string.Format(Properties.Resources.ASM_MISMATCH_LENGTH_FMT, + fi.Length, mProject.FileData.Length); + MessageBox.Show(msg, Properties.Resources.ASM_MISMATCH_CAPTION, + MessageBoxButtons.OK, MessageBoxIcon.Error); + sb.Append(msg); + } else { + string msg = string.Format(Properties.Resources.ASM_MISMATCH_DATA_FMT, + offset, fileVal, mProject.FileData[offset]); + MessageBox.Show(msg, Properties.Resources.ASM_MISMATCH_CAPTION, + MessageBoxButtons.OK, MessageBoxIcon.Error); + sb.Append(msg); + } + } else { + sb.Append(Properties.Resources.ASM_MATCH_SUCCESS); + } + } + sb.Append("\r\n\r\n"); + + if (results.Stdout != null && results.Stdout.Length > 2) { + sb.Append("----- stdout -----\r\n"); + sb.Append(results.Stdout); + sb.Append("\r\n"); + } + if (results.Stderr != null && results.Stderr.Length > 2) { + sb.Append("----- stderr -----\r\n"); + sb.Append(results.Stderr); + sb.Append("\r\n"); + } + + cmdOutputTextBox.Text = sb.ToString(); + cmdOutputTextBox.BackColor = SystemColors.Window; + } + } +} diff --git a/SourceGen/AsmGen/GenAndAsm.resx b/SourceGen/AsmGen/GenAndAsm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SourceGen/AsmGen/GenAndAsm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SourceGen/AsmGen/GenCc65.cs b/SourceGen/AsmGen/GenCc65.cs new file mode 100644 index 0000000..1d27371 --- /dev/null +++ b/SourceGen/AsmGen/GenCc65.cs @@ -0,0 +1,637 @@ +/* + * Copyright 2018 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 System.Diagnostics; +using System.IO; +using System.Text; + +using Asm65; +using CommonUtil; + +namespace SourceGen.AsmGen { + /// + /// Generate source code compatible with the cc65 assembler (https://github.com/cc65/cc65). + /// + public class GenCc65 : IGenerator { + private const string ASM_FILE_SUFFIX = "_cc65.S"; + private const int MAX_OPERAND_LEN = 64; + + // IGenerator + public DisasmProject Project { get; private set; } + + // IGenerator + public Formatter SourceFormatter { get; private set; } + + // IGenerator + public AppSettings Settings { get; private set; } + + // IGenerator + public AssemblerQuirks Quirks { get; private set; } + + // IGenerator + public LabelLocalizer Localizer { get { return mLocalizer; } } + + /// + /// Working directory, i.e. where we write our output file(s). + /// + private string mWorkDirectory; + + /// + /// If set, long labels get their own line. + /// + private bool mLongLabelNewLine; + + /// + /// Base filename. Typically the project file name without the ".dis65" extension. + /// + private string mFileNameBase; + + /// + /// StringBuilder to use when composing a line. Held here to reduce allocations. + /// + private StringBuilder mLineBuilder = new StringBuilder(100); + + /// + /// Label localization helper. + /// + private LabelLocalizer mLocalizer; + + /// + /// Stream to send the output to. + /// + private StreamWriter mOutStream; + + /// + /// The first time we output a high-ASCII string, we generate a macro for it. + /// + private bool mHighAsciiMacroOutput; + + /// + /// Holds detected version of configured assembler. + /// + private CommonUtil.Version mAsmVersion = CommonUtil.Version.NO_VERSION; + + // We test against this in a few places. + private static CommonUtil.Version V2_17 = new CommonUtil.Version(2, 17); + + + // Pseudo-op string constants. + private static PseudoOp.PseudoOpNames sDataOpNames = new PseudoOp.PseudoOpNames() { + EquDirective = "=", + OrgDirective = ".org", + //RegWidthDirective // .a8, .a16, .i8, .i16 + DefineData1 = ".byte", + DefineData2 = ".word", + DefineData3 = ".faraddr", + DefineData4 = ".dword", + DefineBigData2 = ".dbyt", + //DefineBigData3 + //DefineBigData4 + Fill = ".res", + //Dense // no equivalent, use .byte with comma-separated args + StrGeneric = ".byte", + //StrReverse + StrNullTerm = ".asciiz", + //StrLen8 // macro with .strlen? + //StrLen16 + //StrDci + //StrDciReverse + }; + + + // IGenerator + public void Configure(DisasmProject project, string workDirectory, string fileNameBase, + AssemblerVersion asmVersion, AppSettings settings) { + Debug.Assert(project != null); + Debug.Assert(!string.IsNullOrEmpty(workDirectory)); + Debug.Assert(!string.IsNullOrEmpty(fileNameBase)); + + Project = project; + Quirks = new AssemblerQuirks(); + if (asmVersion != null) { + mAsmVersion = asmVersion.Version; + if (mAsmVersion <= V2_17) { + // cc65 v2.17: https://github.com/cc65/cc65/issues/717 + Quirks.BlockMoveArgsReversed = true; + // cc65 v2.17: https://github.com/cc65/cc65/issues/754 + Quirks.NoPcRelBankWrap = true; + } + } + + mWorkDirectory = workDirectory; + mFileNameBase = fileNameBase; + Settings = settings; + + mLongLabelNewLine = Settings.GetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, false); + } + + // IGenerator + public List GenerateSource(BackgroundWorker worker) { + List pathNames = new List(1); + + string fileName = mFileNameBase + ASM_FILE_SUFFIX; + string pathName = Path.Combine(mWorkDirectory, fileName); + pathNames.Add(pathName); + + Formatter.FormatConfig config = new Formatter.FormatConfig(); + GenCommon.ConfigureFormatterFromSettings(Settings, ref config); + config.mForceAbsOpcodeSuffix = string.Empty; + config.mForceLongOpcodeSuffix = string.Empty; + config.mForceAbsOperandPrefix = "a:"; // absolute + config.mForceLongOperandPrefix = "f:"; // far + config.mEndOfLineCommentDelimiter = ";"; + config.mFullLineCommentDelimiterBase = ";"; + config.mBoxLineCommentDelimiter = ";"; + config.mAllowHighAsciiCharConst = false; + config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Simple; + SourceFormatter = new Formatter(config); + + string msg = string.Format(Properties.Resources.PROGRESS_GENERATING_FMT, pathName); + worker.ReportProgress(0, msg); + + mLocalizer = new LabelLocalizer(Project); + if (!Settings.GetBool(AppSettings.SRCGEN_DISABLE_LABEL_LOCALIZATION, false)) { + mLocalizer.LocalPrefix = "@"; + mLocalizer.Analyze(); + } + + // Use UTF-8 encoding, without a byte-order mark. + using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) { + mOutStream = sw; + + if (Settings.GetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, false)) { + if (mAsmVersion.IsValid && mAsmVersion <= V2_17) { + OutputLine(SourceFormatter.FullLineCommentDelimiter + + string.Format(Properties.Resources.GENERATED_FOR_VERSION, + "cc65", mAsmVersion.ToString())); + } else { + OutputLine(SourceFormatter.FullLineCommentDelimiter + + string.Format(Properties.Resources.GENERATED_FOR_LATEST, "cc65")); + } + } + + GenCommon.Generate(this, sw, worker); + } + mOutStream = null; + + return pathNames; + } + + // IGenerator + public void OutputAsmConfig() { + CpuDef cpuDef = Project.CpuDef; + string cpuStr; + if (cpuDef.Type == CpuDef.CpuType.Cpu65816) { + cpuStr = "65816"; + } else if (cpuDef.Type == CpuDef.CpuType.Cpu65C02) { + cpuStr = "65C02"; + } else if (cpuDef.Type == CpuDef.CpuType.Cpu6502 && cpuDef.HasUndocumented) { + cpuStr = "6502X"; + } else { + cpuStr = "6502"; + } + + OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(".setcpu"), + '\"' + cpuStr + '\"', string.Empty); + } + + /// + /// Map the mnemonics we chose for undocumented opcodes to the cc65 mnemonics. + /// + /// We don't include the double- and triple-byte NOPs here, as cc65 doesn't + /// appear to have a definition for them (as of 2.17). + /// + private static Dictionary sUndocMap = new Dictionary() { + { OpName.ASR, "alr" }, // imm 0x4b + { OpName.ANC, "anc" }, // imm 0x0b (and others) + { OpName.ANE, "ane" }, // imm 0x8b + { OpName.ARR, "arr" }, // imm 0x6b + { OpName.SBX, "axs" }, // imm 0xcb + { OpName.DCP, "dcp" }, // abs 0xcf + { OpName.ISB, "isc" }, // abs 0xef + { OpName.HLT, "jam" }, // abs 0x02 (and others) + { OpName.LAE, "las" }, // abs,y 0xbb + { OpName.LXA, "lax" }, // imm 0xab + { OpName.RLA, "rla" }, // abs 0x2f + { OpName.RRA, "rra" }, // abs 0x6f + { OpName.SAX, "sax" }, // abs 0x8f + { OpName.SHA, "sha" }, // abs,y 0x9f + { OpName.SHX, "shx" }, // abs,y 0x9e + { OpName.SHY, "shy" }, // abs,x 0x9c + { OpName.SLO, "slo" }, // abs 0x0f + { OpName.SRE, "sre" }, // abs 0x4f + { OpName.SHS, "tas" }, // abs,y 0x9b + }; + + // IGenerator + public string ReplaceMnemonic(OpDef op) { + if ((op == OpDef.OpWDM_WDM || op == OpDef.OpBRK_StackInt) && mAsmVersion <= V2_17) { + // cc65 v2.17 doesn't support WDM, and assembles BRK to opcode $05. + // https://github.com/cc65/cc65/issues/715 + // https://github.com/cc65/cc65/issues/716 + return null; + } else if (op.IsUndocumented) { + if (sUndocMap.TryGetValue(op.Mnemonic, out string newValue)) { + if ((op.Mnemonic == OpName.ANC && op.Opcode != 0x0b) || + (op.Mnemonic == OpName.HLT && op.Opcode != 0x02)) { + // There are multiple opcodes for the same thing. cc65 outputs + // one specific thing, so we need to match that, and just do a hex + // dump for the others. + return null; + } + return newValue; + } + return null; + } else { + return string.Empty; + } + } + + // IGenerator + public void GenerateShortSequence(int offset, int length, out string opcode, + out string operand) { + Debug.Assert(length >= 1 && length <= 4); + + // Use a comma-separated list of individual hex bytes. + opcode = sDataOpNames.DefineData1; + + StringBuilder sb = new StringBuilder(length * 4); + for (int i = 0; i < length; i++) { + if (i != 0) { + sb.Append(','); + } + sb.Append(SourceFormatter.FormatHexValue(Project.FileData[offset + i], 2)); + } + operand = sb.ToString(); + } + + // IGenerator + public void OutputDataOp(int offset) { + Formatter formatter = SourceFormatter; + byte[] data = Project.FileData; + Anattrib attr = Project.GetAnattrib(offset); + + string labelStr = string.Empty; + if (attr.Symbol != null) { + labelStr = mLocalizer.ConvLabel(attr.Symbol.Label); + } + + string commentStr = SourceFormatter.FormatEolComment(Project.Comments[offset]); + string opcodeStr, operandStr; + + FormatDescriptor dfd = attr.DataDescriptor; + Debug.Assert(dfd != null); + int length = dfd.Length; + Debug.Assert(length > 0); + + bool multiLine = false; + switch (dfd.FormatType) { + case FormatDescriptor.Type.Default: + if (length != 1) { + Debug.Assert(false); + length = 1; + } + opcodeStr = sDataOpNames.DefineData1; + int operand = RawData.GetWord(data, offset, length, false); + operandStr = formatter.FormatHexValue(operand, length * 2); + break; + case FormatDescriptor.Type.NumericLE: + opcodeStr = sDataOpNames.GetDefineData(length); + operand = RawData.GetWord(data, offset, length, false); + operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, + mLocalizer.LabelMap, dfd, operand, length, false); + break; + case FormatDescriptor.Type.NumericBE: + opcodeStr = sDataOpNames.GetDefineBigData(length); + if (opcodeStr == null) { + // Nothing defined, output as comma-separated single-byte values. + GenerateShortSequence(offset, length, out opcodeStr, out operandStr); + } else { + operand = RawData.GetWord(data, offset, length, true); + operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, + mLocalizer.LabelMap, dfd, operand, length, false); + } + break; + case FormatDescriptor.Type.Fill: + opcodeStr = sDataOpNames.Fill; + operandStr = length + "," + formatter.FormatHexValue(data[offset], 2); + break; + case FormatDescriptor.Type.Dense: + multiLine = true; + opcodeStr = operandStr = null; + OutputDenseHex(offset, length, labelStr, commentStr); + break; + case FormatDescriptor.Type.String: + multiLine = true; + opcodeStr = operandStr = null; + OutputString(offset, labelStr, commentStr); + break; + default: + opcodeStr = "???"; + operandStr = "***"; + break; + } + + if (!multiLine) { + opcodeStr = formatter.FormatPseudoOp(opcodeStr); + OutputLine(labelStr, opcodeStr, operandStr, commentStr); + } + } + + private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) { + Formatter formatter = SourceFormatter; + byte[] data = Project.FileData; + StringBuilder sb = new StringBuilder(MAX_OPERAND_LEN); + + string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.DefineData1); + + int maxPerLine = MAX_OPERAND_LEN / 4; + int numChunks = (length + maxPerLine - 1) / maxPerLine; + for (int chunk = 0; chunk < numChunks; chunk++) { + int chunkStart = chunk * maxPerLine; + int chunkEnd = Math.Min((chunk + 1) * maxPerLine, length); + for (int i = chunkStart; i < chunkEnd; i++) { + if (i != chunkStart) { + sb.Append(','); + } + sb.Append(formatter.FormatHexValue(data[offset + i], 2)); + } + + OutputLine(labelStr, opcodeStr, sb.ToString(), commentStr); + labelStr = commentStr = string.Empty; + sb.Clear(); + } + } + + /// + /// Outputs formatted data in an unformatted way, because the code generator couldn't + /// figure out how to do something better. + /// + private void OutputNoJoy(int offset, int length, string labelStr, string commentStr) { + byte[] data = Project.FileData; + Debug.Assert(length > 0); + Debug.Assert(offset >= 0 && offset < data.Length); + + bool singleValue = true; + byte val = data[offset]; + for (int i = 1; i < length; i++) { + if (data[offset + i] != val) { + singleValue = false; + break; + } + } + + if (singleValue) { + string opcodeStr = SourceFormatter.FormatPseudoOp(sDataOpNames.Fill); + string operandStr = length + "," + SourceFormatter.FormatHexValue(val, 2); + OutputLine(labelStr, opcodeStr, operandStr, commentStr); + } else { + OutputDenseHex(offset, length, labelStr, commentStr); + } + } + + // IGenerator + public void OutputEquDirective(string name, string valueStr, string comment) { + OutputLine(name, SourceFormatter.FormatPseudoOp(sDataOpNames.EquDirective), + valueStr, SourceFormatter.FormatEolComment(comment)); + } + + // IGenerator + public void OutputOrgDirective(int address) { + OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), + SourceFormatter.FormatHexValue(address, 4), string.Empty); + } + + // IGenerator + public void OutputRegWidthDirective(int offset, int prevM, int prevX, int newM, int newX) { + if (prevM != newM) { + string mop = (newM == 0) ? ".a16" : ".a8"; + OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(mop), + string.Empty, string.Empty); + } + if (prevX != newX) { + string xop = (newX == 0) ? ".i16" : ".i8"; + OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(xop), + string.Empty, string.Empty); + } + } + + // IGenerator + public void OutputLine(string fullLine) { + mOutStream.WriteLine(fullLine); + } + + // IGenerator + public void OutputLine(string label, string opcode, string operand, string comment) { + // If a label is provided, and it doesn't start with a '.' (indicating that it's + // a directive), and this isn't an EQU directive, add a ':'. Might be easier to + // just ".feature labels_without_colons", but I'm trying to do things the way + // that cc65 users will expect. + if (!string.IsNullOrEmpty(label) && label[0] != '.' && + !String.Equals(opcode, sDataOpNames.EquDirective, + StringComparison.InvariantCultureIgnoreCase)) { + label += ':'; + + if (mLongLabelNewLine && label.Length >= 9) { + mOutStream.WriteLine(label); + label = string.Empty; + } + } + + mLineBuilder.Clear(); + TextUtil.AppendPaddedString(mLineBuilder, label, 9); + TextUtil.AppendPaddedString(mLineBuilder, opcode, 9 + 8); + TextUtil.AppendPaddedString(mLineBuilder, operand, 9 + 8 + 11); + if (string.IsNullOrEmpty(comment)) { + // Trim trailing spaces off of opcode or operand. If they want trailing + // spaces at the end of a comment, that's fine. + CommonUtil.TextUtil.TrimEnd(mLineBuilder); + } else { + mLineBuilder.Append(comment); + } + + mOutStream.WriteLine(mLineBuilder.ToString()); + } + + private void OutputString(int offset, string labelStr, string commentStr) { + // Normal ASCII strings are straightforward: they're just part of a .byte + // directive, and can mix with anything else in the .byte. + // + // For CString we can use .asciiz, but only if the string fits on one line + // and doesn't include delimiters. For L8String and L16String we can + // define simple macros, but their use has a similar restriction. High-ASCII + // strings also require a macro. + // + // We might be able to define a macro for DCI and Reverse as well. + // + // The limitation on strings with delimiters arises because (1) I don't see a + // way to escape them within a string, and (2) the simple macro workarounds + // only take a single argument, not a comma-separated list of stuff. + // + // Some ideas here: + // https://groups.google.com/forum/#!topic/comp.sys.apple2.programmer/5Wkw8mUPcU0 + + Formatter formatter = SourceFormatter; + byte[] data = Project.FileData; + Anattrib attr = Project.GetAnattrib(offset); + FormatDescriptor dfd = attr.DataDescriptor; + Debug.Assert(dfd != null); + Debug.Assert(dfd.FormatType == FormatDescriptor.Type.String); + Debug.Assert(dfd.Length > 0); + + bool highAscii = false; + int leadingBytes = 0; + int trailingBytes = 0; + bool showLeading = false; + bool showTrailing = false; + + switch (dfd.FormatSubType) { + case FormatDescriptor.SubType.None: + highAscii = (data[offset] & 0x80) != 0; + break; + case FormatDescriptor.SubType.Dci: + highAscii = (data[offset] & 0x80) != 0; + trailingBytes = 1; + showTrailing = true; + break; + case FormatDescriptor.SubType.Reverse: + highAscii = (data[offset] & 0x80) != 0; + break; + case FormatDescriptor.SubType.DciReverse: + highAscii = (data[offset + dfd.Length - 1] & 0x80) != 0; + leadingBytes = 1; + showLeading = true; + break; + case FormatDescriptor.SubType.CString: + highAscii = (data[offset] & 0x80) != 0; + trailingBytes = 1; + showTrailing = true; + break; + case FormatDescriptor.SubType.L8String: + if (dfd.Length > 1) { + highAscii = (data[offset + 1] & 0x80) != 0; + } + leadingBytes = 1; + showLeading = true; + break; + case FormatDescriptor.SubType.L16String: + if (dfd.Length > 2) { + highAscii = (data[offset + 2] & 0x80) != 0; + } + leadingBytes = 2; + showLeading = true; + break; + default: + Debug.Assert(false); + return; + } + + char delim = '"'; + StringGather gath = null; + + // Run the string through so we can see if it'll fit on one line. As a minor + // optimization, we skip this step for "generic" strings, which are probably + // the most common thing. + if (dfd.FormatSubType != FormatDescriptor.SubType.None || highAscii) { + gath = new StringGather(this, labelStr, "???", commentStr, delim, + delim, StringGather.ByteStyle.CommaSep, MAX_OPERAND_LEN, true); + FeedGath(gath, data, offset, dfd.Length, leadingBytes, showLeading, + trailingBytes, showTrailing); + Debug.Assert(gath.NumLinesOutput > 0); + } + + string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.StrGeneric); + + switch (dfd.FormatSubType) { + case FormatDescriptor.SubType.None: + // Special case for simple short high-ASCII strings. These have no + // leading or trailing bytes. We can improve this a bit by handling + // arbitrarily long strings by simply breaking them across lines. + Debug.Assert(leadingBytes == 0); + Debug.Assert(trailingBytes == 0); + if (highAscii && gath.NumLinesOutput == 1 && !gath.HasDelimiter) { + if (!mHighAsciiMacroOutput) { + mHighAsciiMacroOutput = true; + // Output a macro for high-ASCII strings. + OutputLine(".macro", "HiAscii", "Arg", string.Empty); + OutputLine(string.Empty, ".repeat", ".strlen(Arg), I", string.Empty); + OutputLine(string.Empty, ".byte", ".strat(Arg, I) | $80", string.Empty); + OutputLine(string.Empty, ".endrep", string.Empty, string.Empty); + OutputLine(".endmacro", string.Empty, string.Empty, string.Empty); + } + opcodeStr = formatter.FormatPseudoOp("HiAscii"); + highAscii = false; + } + break; + case FormatDescriptor.SubType.Dci: + case FormatDescriptor.SubType.Reverse: + case FormatDescriptor.SubType.DciReverse: + // Full configured above. + break; + case FormatDescriptor.SubType.CString: + if (gath.NumLinesOutput == 1 && !gath.HasDelimiter) { + opcodeStr = sDataOpNames.StrNullTerm; + showTrailing = false; + } + break; + case FormatDescriptor.SubType.L8String: + case FormatDescriptor.SubType.L16String: + // Implement macros? + break; + default: + Debug.Assert(false); + return; + } + + if (highAscii) { + OutputNoJoy(offset, dfd.Length, labelStr, commentStr); + return; + } + + // Create a new StringGather, with the final opcode choice. + gath = new StringGather(this, labelStr, opcodeStr, commentStr, delim, + delim, StringGather.ByteStyle.CommaSep, MAX_OPERAND_LEN, false); + FeedGath(gath, data, offset, dfd.Length, leadingBytes, showLeading, + trailingBytes, showTrailing); + } + + /// + /// Feeds the bytes into the StringGather. + /// + private void FeedGath(StringGather gath, byte[] data, int offset, int length, + int leadingBytes, bool showLeading, int trailingBytes, bool showTrailing) { + int startOffset = offset; + int strEndOffset = offset + length - trailingBytes; + + if (showLeading) { + while (leadingBytes-- > 0) { + gath.WriteByte(data[offset++]); + } + } else { + offset += leadingBytes; + } + for (; offset < strEndOffset; offset++) { + gath.WriteChar((char)(data[offset] & 0x7f)); + } + while (showTrailing && trailingBytes-- > 0) { + gath.WriteByte(data[offset++]); + } + gath.Finish(); + } + } +} diff --git a/SourceGen/AsmGen/GenCommon.cs b/SourceGen/AsmGen/GenCommon.cs new file mode 100644 index 0000000..a8e9369 --- /dev/null +++ b/SourceGen/AsmGen/GenCommon.cs @@ -0,0 +1,329 @@ +/* + * Copyright 2018 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 System.Diagnostics; +using System.IO; + +using Asm65; + +namespace SourceGen.AsmGen { + public class GenCommon { + /// + /// Generates assembly source. + /// + /// This code is common to all generators. + /// + /// + /// + /// + /// + public static void Generate(IGenerator gen, StreamWriter sw, BackgroundWorker worker) { + DisasmProject proj = gen.Project; + Formatter formatter = gen.SourceFormatter; + int offset = 0; + + bool doAddCycles = gen.Settings.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, false); + + GenerateHeader(gen, sw); + + // Used for M/X flag tracking. + StatusFlags prevFlags = StatusFlags.AllIndeterminate; + + int lastProgress = 0; + + while (offset < proj.FileData.Length) { + Anattrib attr = proj.GetAnattrib(offset); + + if (attr.IsInstructionStart && offset > 0 && + proj.GetAnattrib(offset - 1).IsData) { + // Transition from data to code. (Don't add blank line for inline data.) + gen.OutputLine(string.Empty); + } + + // Long comments come first. + if (proj.LongComments.TryGetValue(offset, out MultiLineComment longComment)) { + List formatted = longComment.FormatText(formatter, string.Empty); + foreach (string str in formatted) { + gen.OutputLine(str); + } + } + + // Check for address change. + int orgAddr = proj.AddrMap.Get(offset); + if (orgAddr >= 0) { + gen.OutputOrgDirective(orgAddr); + } + + if (attr.IsInstructionStart) { + // Generate M/X reg width directive, if necessary. + // NOTE: we can suppress the initial directive if we know what the + // target assembler's default assumption is. Probably want to handle + // that in the ORG output handler. + if (proj.CpuDef.HasEmuFlag) { + StatusFlags curFlags = attr.StatusFlags; + curFlags.M = attr.StatusFlags.ShortM ? 1 : 0; + curFlags.X = attr.StatusFlags.ShortX ? 1 : 0; + if (curFlags.M != prevFlags.M || curFlags.X != prevFlags.X) { + // changed, output directive + gen.OutputRegWidthDirective(offset, prevFlags.M, prevFlags.X, + curFlags.M, curFlags.X); + } + + prevFlags = curFlags; + } + + // Look for embedded instructions. + int len; + for (len = 1; len < attr.Length; len++) { + if (proj.GetAnattrib(offset + len).IsInstructionStart) { + break; + } + } + + // Output instruction. + GenerateInstruction(gen, sw, offset, len, doAddCycles); + + if (attr.DoesNotContinue) { + gen.OutputLine(string.Empty); + } + + offset += len; + } else { + gen.OutputDataOp(offset); + offset += attr.Length; + } + + // Update progress meter. We don't want to spam it, so just ping it 10x. + int curProgress = (offset * 10) / proj.FileData.Length; + if (lastProgress != curProgress) { + if (worker.CancellationPending) { + Debug.WriteLine("GenCommon got cancellation request"); + return; + } + lastProgress = curProgress; + worker.ReportProgress(curProgress * 10); + //System.Threading.Thread.Sleep(500); + } + } + } + + private static void GenerateHeader(IGenerator gen, StreamWriter sw) { + DisasmProject proj = gen.Project; + Formatter formatter = gen.SourceFormatter; + + // Check for header comment. + if (proj.LongComments.TryGetValue(DisplayList.Line.HEADER_COMMENT_OFFSET, + out MultiLineComment headerComment)) { + List formatted = headerComment.FormatText(formatter, string.Empty); + foreach (string str in formatted) { + gen.OutputLine(str); + } + } + + gen.OutputAsmConfig(); + + // Format symbols. + foreach (DefSymbol defSym in proj.ActiveDefSymbolList) { + // Use an operand length of 1 so things are shown as concisely as possible. + string valueStr = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, + gen.Localizer.LabelMap, defSym.DataDescriptor, defSym.Value, 1, false); + gen.OutputEquDirective(defSym.Label, valueStr, defSym.Comment); + } + + // If there was at least one symbol, output a blank line. + if (proj.ActiveDefSymbolList.Count != 0) { + gen.OutputLine(string.Empty); + } + } + + private static void GenerateInstruction(IGenerator gen, StreamWriter sw, int offset, + int instrBytes, bool doAddCycles) { + DisasmProject proj = gen.Project; + Formatter formatter = gen.SourceFormatter; + byte[] data = proj.FileData; + Anattrib attr = proj.GetAnattrib(offset); + + string labelStr = string.Empty; + if (attr.Symbol != null) { + labelStr = gen.Localizer.ConvLabel(attr.Symbol.Label); + } + + OpDef op = proj.CpuDef.GetOpDef(data[offset]); + int operand = op.GetOperand(data, offset, attr.StatusFlags); + int instrLen = op.GetLength(attr.StatusFlags); + OpDef.WidthDisambiguation wdis = OpDef.WidthDisambiguation.None; + if (op.IsWidthPotentiallyAmbiguous) { + wdis = OpDef.GetWidthDisambiguation(instrLen, operand); + } + + string replMnemonic = gen.ReplaceMnemonic(op); + string opcodeStr = formatter.FormatOpcode(op, wdis); + + string formattedOperand = null; + int operandLen = instrLen - 1; + bool isPcRel = false; + bool isPcRelBankWrap = false; + + // Tweak branch instructions. We want to show the absolute address rather + // than the relative offset (which happens with the OperandAddress assignment + // below), and 1-byte branches should always appear as a 4-byte hex value. + if (op.AddrMode == OpDef.AddressMode.PCRel) { + Debug.Assert(attr.OperandAddress >= 0); + operandLen = 2; + isPcRel = true; + } else if (op.AddrMode == OpDef.AddressMode.PCRelLong || + op.AddrMode == OpDef.AddressMode.StackPCRelLong) { + isPcRel = true; + } + if (isPcRel) { + int branchDist = attr.Address - attr.OperandAddress; + isPcRelBankWrap = branchDist > 32767 || branchDist < -32768; + } + + // 16-bit operands outside bank 0 need to include the bank when computing + // symbol adjustment. + int operandForSymbol = operand; + if (attr.OperandAddress >= 0) { + operandForSymbol = attr.OperandAddress; + } + + // Check Length to watch for bogus descriptors (?) + if (attr.DataDescriptor != null && attr.Length == attr.DataDescriptor.Length) { + // Format operand as directed. + if (op.AddrMode == OpDef.AddressMode.BlockMove) { + // Special handling for the double-operand block move. + string opstr1 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, + gen.Localizer.LabelMap, attr.DataDescriptor, operand >> 8, 1, false); + string opstr2 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, + gen.Localizer.LabelMap, attr.DataDescriptor, operand & 0xff, 1, false); + if (gen.Quirks.BlockMoveArgsReversed) { + string tmp = opstr1; + opstr1 = opstr2; + opstr2 = tmp; + } + formattedOperand = opstr1 + "," + opstr2; + } else { + formattedOperand = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, + gen.Localizer.LabelMap, attr.DataDescriptor, + operandForSymbol, operandLen, isPcRel); + } + } else { + // Show operand value in hex. + if (op.AddrMode == OpDef.AddressMode.BlockMove) { + int arg1, arg2; + if (gen.Quirks.BlockMoveArgsReversed) { + arg1 = operand & 0xff; + arg2 = operand >> 8; + } else { + arg1 = operand >> 8; + arg2 = operand & 0xff; + } + formattedOperand = formatter.FormatHexValue(arg1, 2) + "," + + formatter.FormatHexValue(arg2, 2); + } else { + if (operandLen == 2) { + // This is necessary for 16-bit operands, like "LDA abs" and "PEA val", + // when outside bank zero. The bank is included in the operand address, + // but we don't want to show it here. + operandForSymbol &= 0xffff; + } + formattedOperand = formatter.FormatHexValue(operandForSymbol, operandLen * 2); + } + } + string operandStr = formatter.FormatOperand(op, formattedOperand, wdis); + + string eolComment = proj.Comments[offset]; + if (doAddCycles) { + bool branchCross = (attr.Address & 0xff00) != (operandForSymbol & 0xff00); + int cycles = proj.CpuDef.GetCycles(op.Opcode, attr.StatusFlags, attr.BranchTaken, + branchCross); + if (cycles > 0) { + eolComment = cycles.ToString() + " " + eolComment; + } else { + eolComment = (-cycles).ToString() + "+ " + eolComment; + } + } + string commentStr = formatter.FormatEolComment(eolComment); + + if (attr.Length != instrBytes) { + // This instruction has another instruction inside it. Throw out what we + // computed and just output as bytes. + gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); + } else if (isPcRelBankWrap && gen.Quirks.NoPcRelBankWrap) { + // Some assemblers have trouble generating PC-relative operands that wrap + // around the bank. Output as raw hex. + gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); + } else if (op.AddrMode == OpDef.AddressMode.BlockMove && + gen.Quirks.BlockMoveArgsReversed) { + // On second thought, just don't even output the wrong thing. + gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); + } else if (replMnemonic == null) { + // No mnemonic exists for this opcode. + gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); + } else if (replMnemonic != string.Empty) { + // A replacement mnemonic has been provided. + opcodeStr = formatter.FormatMnemonic(replMnemonic, wdis); + } + gen.OutputLine(labelStr, opcodeStr, operandStr, commentStr); + + // Assemblers like Merlin32 try to be helpful and track SEP/REP, but they do the + // wrong thing if we're in emulation mode. Force flags back to short. + if (proj.CpuDef.HasEmuFlag && gen.Quirks.TracksSepRepNotEmu && op == OpDef.OpREP_Imm) { + if ((operand & 0x30) != 0 && attr.StatusFlags.E == 1) { + gen.OutputRegWidthDirective(offset, 0, 0, 1, 1); + } + } + } + + /// + /// Configures some common format config items from the app settings. Uses a + /// passed-in object, rather than the global settings. + /// + /// Application settings. + /// Format config struct. + public static void ConfigureFormatterFromSettings(AppSettings settings, + ref Formatter.FormatConfig config) { + config.mUpperHexDigits = + settings.GetBool(AppSettings.FMT_UPPER_HEX_DIGITS, false); + config.mUpperOpcodes = + settings.GetBool(AppSettings.FMT_UPPER_OP_MNEMONIC, false); + config.mUpperPseudoOpcodes = + settings.GetBool(AppSettings.FMT_UPPER_PSEUDO_OP_MNEMONIC, false); + config.mUpperOperandA = + settings.GetBool(AppSettings.FMT_UPPER_OPERAND_A, false); + config.mUpperOperandS = + settings.GetBool(AppSettings.FMT_UPPER_OPERAND_S, false); + config.mUpperOperandXY = + settings.GetBool(AppSettings.FMT_UPPER_OPERAND_XY, false); + config.mAddSpaceLongComment = + settings.GetBool(AppSettings.FMT_ADD_SPACE_FULL_COMMENT, true); + + config.mForceAbsOpcodeSuffix = + settings.GetString(AppSettings.FMT_OPCODE_SUFFIX_ABS, string.Empty); + config.mForceLongOpcodeSuffix = + settings.GetString(AppSettings.FMT_OPCODE_SUFFIX_LONG, string.Empty); + config.mForceAbsOperandPrefix = + settings.GetString(AppSettings.FMT_OPERAND_PREFIX_ABS, string.Empty); + config.mForceLongOperandPrefix = + settings.GetString(AppSettings.FMT_OPERAND_PREFIX_LONG, string.Empty); + + string exprMode = settings.GetString(AppSettings.FMT_EXPRESSION_MODE, string.Empty); + config.mExpressionMode = Formatter.FormatConfig.ParseExpressionMode(exprMode); + } + } +} diff --git a/SourceGen/AsmGen/GenMerlin32.cs b/SourceGen/AsmGen/GenMerlin32.cs new file mode 100644 index 0000000..aeb83da --- /dev/null +++ b/SourceGen/AsmGen/GenMerlin32.cs @@ -0,0 +1,594 @@ +/* + * Copyright 2018 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 System.Diagnostics; +using System.IO; +using System.Text; + +using Asm65; +using CommonUtil; + +namespace SourceGen.AsmGen { + /// + /// Generate source code compatible with Brutal Deluxe's Merlin 32 assembler + /// (https://www.brutaldeluxe.fr/products/crossdevtools/merlin/). + /// + public class GenMerlin32 : IGenerator { + private const string ASM_FILE_SUFFIX = "_Merlin32.S"; + private const int MAX_OPERAND_LEN = 64; + + // IGenerator + public DisasmProject Project { get; private set; } + + // IGenerator + public Formatter SourceFormatter { get; private set; } + + // IGenerator + public AppSettings Settings { get; private set; } + + // IGenerator + public AssemblerQuirks Quirks { get; private set; } + + // IGenerator + public LabelLocalizer Localizer { get { return mLocalizer; } } + + /// + /// Working directory, i.e. where we write our output file(s). + /// + private string mWorkDirectory; + + /// + /// If set, long labels get their own line. + /// + private bool mLongLabelNewLine; + + /// + /// Base filename. Typically the project file name without the ".dis65" extension. + /// + private string mFileNameBase; + + /// + /// StringBuilder to use when composing a line. Held here to reduce allocations. + /// + private StringBuilder mLineBuilder = new StringBuilder(100); + + /// + /// Label localization helper. + /// + private LabelLocalizer mLocalizer; + + /// + /// Stream to send the output to. + /// + private StreamWriter mOutStream; + + /// + /// Holds detected version of configured assembler. + /// + private CommonUtil.Version mAsmVersion = CommonUtil.Version.NO_VERSION; + + + // Semi-convenient way to hold all the interesting string constants in one place. + // Note the actual usage of the pseudo-op may not match what the main app does, + // e.g. RegWidthDirective behaves differently from "mx". I'm just trying to avoid + // having string constants scattered all over. + private static PseudoOp.PseudoOpNames sDataOpNames = new PseudoOp.PseudoOpNames() { + EquDirective = "equ", + OrgDirective = "org", + RegWidthDirective = "mx", + DefineData1 = "dfb", + DefineData2 = "dw", + DefineData3 = "adr", + DefineData4 = "adrl", + DefineBigData2 = "ddb", + //DefineBigData3 + //DefineBigData4 + Fill = "ds", + Dense = "hex", + StrGeneric = "asc", + StrGenericHi = "asc", + StrReverse = "rev", + StrReverseHi = "rev", + //StrNullTerm + StrLen8 = "str", + StrLen8Hi = "str", + StrLen16 = "strl", + StrLen16Hi = "strl", + StrDci = "dci", + StrDciHi = "dci", + //StrDciReverse + }; + + + // IGenerator + public void Configure(DisasmProject project, string workDirectory, string fileNameBase, + AssemblerVersion asmVersion, AppSettings settings) { + Debug.Assert(project != null); + Debug.Assert(!string.IsNullOrEmpty(workDirectory)); + Debug.Assert(!string.IsNullOrEmpty(fileNameBase)); + + Project = project; + Quirks = new AssemblerQuirks(); + Quirks.TracksSepRepNotEmu = true; + Quirks.NoPcRelBankWrap = true; + + mWorkDirectory = workDirectory; + mFileNameBase = fileNameBase; + Settings = settings; + + mLongLabelNewLine = Settings.GetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, false); + } + + // IGenerator; executes on background thread + public List GenerateSource(BackgroundWorker worker) { + List pathNames = new List(1); + + string fileName = mFileNameBase + ASM_FILE_SUFFIX; + string pathName = Path.Combine(mWorkDirectory, fileName); + pathNames.Add(pathName); + + Formatter.FormatConfig config = new Formatter.FormatConfig(); + GenCommon.ConfigureFormatterFromSettings(Settings, ref config); + config.mForceAbsOpcodeSuffix = ":"; + config.mForceLongOpcodeSuffix = "l"; + config.mForceAbsOperandPrefix = string.Empty; + config.mForceLongOperandPrefix = string.Empty; + config.mEndOfLineCommentDelimiter = ";"; + config.mFullLineCommentDelimiterBase = ";"; + config.mBoxLineCommentDelimiter = string.Empty; + config.mAllowHighAsciiCharConst = true; + config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Merlin; + SourceFormatter = new Formatter(config); + + string msg = string.Format(Properties.Resources.PROGRESS_GENERATING_FMT, pathName); + worker.ReportProgress(0, msg); + + mLocalizer = new LabelLocalizer(Project); + if (!Settings.GetBool(AppSettings.SRCGEN_DISABLE_LABEL_LOCALIZATION, false)) { + mLocalizer.LocalPrefix = ":"; + mLocalizer.Analyze(); + } + + // Use UTF-8 encoding, without a byte-order mark. + using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) { + mOutStream = sw; + + if (Settings.GetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, false)) { + // No version-specific stuff yet. + OutputLine(SourceFormatter.FullLineCommentDelimiter + + string.Format(Properties.Resources.GENERATED_FOR_LATEST, "Merlin 32")); + } + + GenCommon.Generate(this, sw, worker); + } + mOutStream = null; + + return pathNames; + } + + // IGenerator + public void OutputDataOp(int offset) { + Formatter formatter = SourceFormatter; + byte[] data = Project.FileData; + Anattrib attr = Project.GetAnattrib(offset); + + string labelStr = string.Empty; + if (attr.Symbol != null) { + labelStr = mLocalizer.ConvLabel(attr.Symbol.Label); + } + + string commentStr = SourceFormatter.FormatEolComment(Project.Comments[offset]); + string opcodeStr, operandStr; + + FormatDescriptor dfd = attr.DataDescriptor; + Debug.Assert(dfd != null); + int length = dfd.Length; + Debug.Assert(length > 0); + + bool multiLine = false; + switch (dfd.FormatType) { + case FormatDescriptor.Type.Default: + if (length != 1) { + Debug.Assert(false); + length = 1; + } + opcodeStr = sDataOpNames.DefineData1; + int operand = RawData.GetWord(data, offset, length, false); + operandStr = formatter.FormatHexValue(operand, length * 2); + break; + case FormatDescriptor.Type.NumericLE: + opcodeStr = sDataOpNames.GetDefineData(length); + operand = RawData.GetWord(data, offset, length, false); + operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, + mLocalizer.LabelMap, dfd, operand, length, false); + break; + case FormatDescriptor.Type.NumericBE: + opcodeStr = sDataOpNames.GetDefineBigData(length); + if (opcodeStr == null) { + // Nothing defined, output as comma-separated single-byte values. + GenerateShortSequence(offset, length, out opcodeStr, out operandStr); + } else { + operand = RawData.GetWord(data, offset, length, true); + operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, + mLocalizer.LabelMap, dfd, operand, length, false); + } + break; + case FormatDescriptor.Type.Fill: + opcodeStr = sDataOpNames.Fill; + operandStr = length + "," + formatter.FormatHexValue(data[offset], 2); + break; + case FormatDescriptor.Type.Dense: + multiLine = true; + opcodeStr = operandStr = null; + OutputDenseHex(offset, length, labelStr, commentStr); + break; + case FormatDescriptor.Type.String: + multiLine = true; + opcodeStr = operandStr = null; + OutputString(offset, labelStr, commentStr); + break; + default: + opcodeStr = "???"; + operandStr = "***"; + break; + } + + if (!multiLine) { + opcodeStr = formatter.FormatPseudoOp(opcodeStr); + OutputLine(labelStr, opcodeStr, operandStr, commentStr); + } + } + + private void OutputDenseHex(int offset, int length, string labelStr, string commentStr) { + Formatter formatter = SourceFormatter; + byte[] data = Project.FileData; + int maxPerLine = MAX_OPERAND_LEN / 2; + + string opcodeStr = formatter.FormatPseudoOp(sDataOpNames.Dense); + for (int i = 0; i < length; i += maxPerLine) { + int subLen = length - i; + if (subLen > maxPerLine) { + subLen = maxPerLine; + } + string operandStr = formatter.FormatDenseHex(data, offset + i, subLen); + + OutputLine(labelStr, opcodeStr, operandStr, commentStr); + labelStr = commentStr = string.Empty; + } + } + + // IGenerator + public string ReplaceMnemonic(OpDef op) { + if (op.IsUndocumented) { + return null; + } else { + return string.Empty; + } + } + + // IGenerator + public void GenerateShortSequence(int offset, int length, out string opcode, + out string operand) { + Debug.Assert(length >= 1 && length <= 4); + + // Use a comma-separated list of individual hex bytes. + opcode = sDataOpNames.DefineData1; + + StringBuilder sb = new StringBuilder(length * 4); + for (int i = 0; i < length; i++) { + if (i != 0) { + sb.Append(','); + } + sb.Append(SourceFormatter.FormatHexValue(Project.FileData[offset + i], 2)); + } + operand = sb.ToString(); + } + + // IGenerator + public void OutputAsmConfig() { + // nothing to do + } + + // IGenerator + public void OutputEquDirective(string name, string valueStr, string comment) { + OutputLine(name, SourceFormatter.FormatPseudoOp(sDataOpNames.EquDirective), + valueStr, SourceFormatter.FormatEolComment(comment)); + } + + // IGenerator + public void OutputOrgDirective(int address) { + OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.OrgDirective), + SourceFormatter.FormatHexValue(address, 4), string.Empty); + } + + // IGenerator + public void OutputRegWidthDirective(int offset, int prevM, int prevX, int newM, int newX) { + // prevM/prevX may be ambiguous for offset 0, but otherwise everything + // should be either 0 or 1. + Debug.Assert(newM == 0 || newM == 1); + Debug.Assert(newX == 0 || newX == 1); + + if (offset == 0 && newM == 1 && newX == 1) { + // Assembler defaults to short regs, so we can skip this. + return; + } + OutputLine(string.Empty, + SourceFormatter.FormatPseudoOp(sDataOpNames.RegWidthDirective), + "%" + newM + newX, string.Empty); + } + + // IGenerator + public void OutputLine(string fullLine) { + mOutStream.WriteLine(fullLine); + } + + // IGenerator + public void OutputLine(string label, string opcode, string operand, string comment) { + // Split long label, but not on EQU directives (confuses the assembler). + if (mLongLabelNewLine && label.Length >= 9 && + !String.Equals(opcode, sDataOpNames.EquDirective, + StringComparison.InvariantCultureIgnoreCase)) { + mOutStream.WriteLine(label); + label = string.Empty; + } + + mLineBuilder.Clear(); + TextUtil.AppendPaddedString(mLineBuilder, label, 9); + TextUtil.AppendPaddedString(mLineBuilder, opcode, 9 + 6); + TextUtil.AppendPaddedString(mLineBuilder, operand, 9 + 6 + 11); + if (string.IsNullOrEmpty(comment)) { + // Trim trailing spaces off of opcode or operand. If they want trailing + // spaces at the end of a comment, that's fine. + CommonUtil.TextUtil.TrimEnd(mLineBuilder); + } else { + mLineBuilder.Append(comment); + } + + mOutStream.WriteLine(mLineBuilder.ToString()); + } + + + private enum RevMode { Forward, Reverse, BlockReverse }; + + private void OutputString(int offset, string labelStr, string commentStr) { + // This gets complicated. + // + // For Dci, L8String, and L16String, the entire string needs to fit in the + // operand of one line. If it can't, we need to separate the length byte/word + // or inverted character out, and just dump the rest as ASCII. Computing the + // line length requires factoring delimiter character escapes. (NOTE: contrary + // to the documentation, STR and STRL do include trailing hex characters in the + // length calculation, so it's possible to escape delimiters.) + // + // For Reverse, we can span lines, but only if we emit the lines in + // backward order. Also, Merlin doesn't allow hex to be embedded in a REV + // operation, so we can't use REV if the string contains a delimiter. + // + // DciReverse is deprecated, but we can handle it as a Reverse string with a + // trailing byte on a following line. + // + // For aesthetic purposes, zero-length CString, L8String, and L16String + // should be output as DFB/DW zeroes rather than an empty string -- makes + // it easier to read. + + Formatter formatter = SourceFormatter; + byte[] data = Project.FileData; + Anattrib attr = Project.GetAnattrib(offset); + FormatDescriptor dfd = attr.DataDescriptor; + Debug.Assert(dfd != null); + Debug.Assert(dfd.FormatType == FormatDescriptor.Type.String); + Debug.Assert(dfd.Length > 0); + + bool highAscii = false; + int showZeroes = 0; + int leadingBytes = 0; + int trailingBytes = 0; + bool showLeading = false; + bool showTrailing = false; + RevMode revMode = RevMode.Forward; + + switch (dfd.FormatSubType) { + case FormatDescriptor.SubType.None: + highAscii = (data[offset] & 0x80) != 0; + break; + case FormatDescriptor.SubType.Dci: + highAscii = (data[offset] & 0x80) != 0; + break; + case FormatDescriptor.SubType.Reverse: + highAscii = (data[offset] & 0x80) != 0; + revMode = RevMode.Reverse; + break; + case FormatDescriptor.SubType.DciReverse: + highAscii = (data[offset + dfd.Length - 1] & 0x80) != 0; + revMode = RevMode.Reverse; + break; + case FormatDescriptor.SubType.CString: + highAscii = (data[offset] & 0x80) != 0; + if (dfd.Length == 1) { + showZeroes = 1; // empty null-terminated string + } + trailingBytes = 1; + showTrailing = true; + break; + case FormatDescriptor.SubType.L8String: + if (dfd.Length > 1) { + highAscii = (data[offset + 1] & 0x80) != 0; + } else { + //showZeroes = 1; + } + leadingBytes = 1; + break; + case FormatDescriptor.SubType.L16String: + if (dfd.Length > 2) { + highAscii = (data[offset + 2] & 0x80) != 0; + } else { + //showZeroes = 2; + } + leadingBytes = 2; + break; + default: + Debug.Assert(false); + return; + } + + if (showZeroes != 0) { + // Empty string. Just output the length byte(s) or null terminator. + GenerateShortSequence(offset, showZeroes, out string opcode, out string operand); + OutputLine(labelStr, opcode, operand, commentStr); + return; + } + + // Merlin 32 uses single-quote for low ASCII, double-quote for high ASCII. When + // quoting the delimiter we use a hexadecimal value. We need to bear in mind that + // we're forcing the characters to low ASCII, but the actual character being + // escaped might be in high ASCII. Hence delim vs. delimReplace. + char delim = highAscii ? '"' : '\''; + char delimReplace = highAscii ? ((char)(delim | 0x80)) : delim; + StringGather gath = null; + + // Run the string through so we can see if it'll fit on one line. As a minor + // optimization, we skip this step for "generic" strings, which are probably + // the most common thing. + if (dfd.FormatSubType != FormatDescriptor.SubType.None) { + gath = new StringGather(this, labelStr, "???", commentStr, delim, + delimReplace, StringGather.ByteStyle.DenseHex, MAX_OPERAND_LEN, true); + FeedGath(gath, data, offset, dfd.Length, revMode, leadingBytes, showLeading, + trailingBytes, showTrailing); + Debug.Assert(gath.NumLinesOutput > 0); + } + + string opcodeStr; + + switch (dfd.FormatSubType) { + case FormatDescriptor.SubType.None: + opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric; + break; + case FormatDescriptor.SubType.Dci: + if (gath.NumLinesOutput == 1) { + opcodeStr = highAscii ? sDataOpNames.StrDciHi : sDataOpNames.StrDci; + } else { + opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric; + trailingBytes = 1; + showTrailing = true; + } + break; + case FormatDescriptor.SubType.Reverse: + if (gath.HasDelimiter) { + // can't include escaped delimiters in REV + opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric; + revMode = RevMode.Forward; + } else if (gath.NumLinesOutput > 1) { + opcodeStr = highAscii ? sDataOpNames.StrReverseHi : sDataOpNames.StrReverse; + revMode = RevMode.BlockReverse; + } else { + opcodeStr = highAscii ? sDataOpNames.StrReverseHi : sDataOpNames.StrReverse; + Debug.Assert(revMode == RevMode.Reverse); + } + break; + case FormatDescriptor.SubType.DciReverse: + // Mostly punt -- output as ASCII with special handling for first byte. + opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric; + revMode = RevMode.Forward; + leadingBytes = 1; + showLeading = true; + break; + case FormatDescriptor.SubType.CString: + //opcodeStr = sDataOpNames.StrNullTerm[highAscii ? 1 : 0]; + opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric; + break; + case FormatDescriptor.SubType.L8String: + if (gath.NumLinesOutput == 1) { + opcodeStr = highAscii ? sDataOpNames.StrLen8Hi : sDataOpNames.StrLen8; + } else { + opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric; + leadingBytes = 1; + showLeading = true; + } + break; + case FormatDescriptor.SubType.L16String: + if (gath.NumLinesOutput == 1) { + opcodeStr = highAscii ? sDataOpNames.StrLen16Hi : sDataOpNames.StrLen16; + } else { + opcodeStr = highAscii ? sDataOpNames.StrGenericHi : sDataOpNames.StrGeneric; + leadingBytes = 2; + showLeading = true; + } + break; + default: + Debug.Assert(false); + return; + } + + opcodeStr = formatter.FormatPseudoOp(opcodeStr); + + // Create a new StringGather, with the final opcode choice. + gath = new StringGather(this, labelStr, opcodeStr, commentStr, delim, + delimReplace, StringGather.ByteStyle.DenseHex, MAX_OPERAND_LEN, false); + FeedGath(gath, data, offset, dfd.Length, revMode, leadingBytes, showLeading, + trailingBytes, showTrailing); + } + + /// + /// Feeds the bytes into the StringGather. + /// + private void FeedGath(StringGather gath, byte[] data, int offset, int length, + RevMode revMode, int leadingBytes, bool showLeading, + int trailingBytes, bool showTrailing) { + int startOffset = offset; + int strEndOffset = offset + length - trailingBytes; + + if (showLeading) { + while (leadingBytes-- > 0) { + gath.WriteByte(data[offset++]); + } + } else { + offset += leadingBytes; + } + if (revMode == RevMode.BlockReverse) { + const int maxPerLine = MAX_OPERAND_LEN - 2; + int numBlockLines = (length + maxPerLine - 1) / maxPerLine; + + for (int chunk = 0; chunk < numBlockLines; chunk++) { + int chunkOffset = startOffset + chunk * maxPerLine; + int endOffset = chunkOffset + maxPerLine; + if (endOffset > strEndOffset) { + endOffset = strEndOffset; + } + for (int off = endOffset - 1; off >= chunkOffset; off--) { + gath.WriteChar((char)(data[off] & 0x7f)); + } + } + } else { + for (; offset < strEndOffset; offset++) { + if (revMode == RevMode.Forward) { + gath.WriteChar((char)(data[offset] & 0x7f)); + } else if (revMode == RevMode.Reverse) { + int posn = startOffset + (strEndOffset - offset) - 1; + gath.WriteChar((char)(data[posn] & 0x7f)); + } else { + Debug.Assert(false); + } + } + } + while (showTrailing && trailingBytes-- > 0) { + gath.WriteByte(data[offset++]); + } + gath.Finish(); + } + } +} diff --git a/SourceGen/AsmGen/GeneratorProgress.Designer.cs b/SourceGen/AsmGen/GeneratorProgress.Designer.cs new file mode 100644 index 0000000..43911d7 --- /dev/null +++ b/SourceGen/AsmGen/GeneratorProgress.Designer.cs @@ -0,0 +1,114 @@ +/* + * Copyright 2018 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. + */ +namespace SourceGen.AsmGen { + partial class GeneratorProgress { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.progressLabel = new System.Windows.Forms.Label(); + this.cancelButton = new System.Windows.Forms.Button(); + this.progressBar1 = new System.Windows.Forms.ProgressBar(); + this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker(); + this.SuspendLayout(); + // + // progressLabel + // + this.progressLabel.AutoSize = true; + this.progressLabel.Location = new System.Drawing.Point(13, 13); + this.progressLabel.Name = "progressLabel"; + this.progressLabel.Size = new System.Drawing.Size(61, 13); + this.progressLabel.TabIndex = 1; + this.progressLabel.Text = "Preparing..."; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.cancelButton.Location = new System.Drawing.Point(275, 79); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 0; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); + // + // progressBar1 + // + this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.progressBar1.Location = new System.Drawing.Point(12, 39); + this.progressBar1.Name = "progressBar1"; + this.progressBar1.Size = new System.Drawing.Size(600, 23); + this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Continuous; + this.progressBar1.TabIndex = 2; + // + // backgroundWorker1 + // + this.backgroundWorker1.WorkerReportsProgress = true; + this.backgroundWorker1.WorkerSupportsCancellation = true; + this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork); + this.backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker1_ProgressChanged); + this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted); + // + // GeneratorProgress + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(624, 114); + this.Controls.Add(this.progressBar1); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.progressLabel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "GeneratorProgress"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Generating Source..."; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.GeneratorProgress_FormClosing); + this.Load += new System.EventHandler(this.GeneratorProgress_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label progressLabel; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.ProgressBar progressBar1; + private System.ComponentModel.BackgroundWorker backgroundWorker1; + } +} \ No newline at end of file diff --git a/SourceGen/AsmGen/GeneratorProgress.cs b/SourceGen/AsmGen/GeneratorProgress.cs new file mode 100644 index 0000000..d6a8dae --- /dev/null +++ b/SourceGen/AsmGen/GeneratorProgress.cs @@ -0,0 +1,124 @@ +/* + * Copyright 2018 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 System.Diagnostics; +using System.Windows.Forms; + +namespace SourceGen.AsmGen { + /// + /// Dialog that shows the progress of the assembler, and allows cancellation. + /// + public partial class GeneratorProgress : Form { + /// + /// Full pathnames of generated files. Will be null on error or user cancelation. + /// + public List Results { get; private set; } + + private IGenerator mGenerator; + + + /// + /// Constructor. + /// + /// Fully-configured source generator. + public GeneratorProgress(IGenerator gen) { + InitializeComponent(); + + mGenerator = gen; + } + + private void GeneratorProgress_Load(object sender, EventArgs e) { + backgroundWorker1.RunWorkerAsync(); + } + + private void cancelButton_Click(object sender, EventArgs e) { + backgroundWorker1.CancelAsync(); + cancelButton.Enabled = false; + } + + private void GeneratorProgress_FormClosing(object sender, FormClosingEventArgs e) { + // The close button will close the dialog without canceling the event. We + // cancel it here, which should cause it to stop relatively quickly, but we don't + // wait for it on the off chance that something weird is going on and it got + // stuck. If nothing else, this gives the user a chance to save their work. + if (backgroundWorker1.IsBusy) { + backgroundWorker1.CancelAsync(); + DialogResult = DialogResult.Cancel; + } + } + + // NOTE: executes on work thread. DO NOT do any UI work here. DO NOT access + // the Results property directly. + private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { + BackgroundWorker worker = sender as BackgroundWorker; + + // This will throw an I/O exception if there's a problem with the file. This + // will be caught and transferred to RunWorkerCompleted. + List fileNames = mGenerator.GenerateSource(worker); + if (worker.CancellationPending) { + e.Cancel = true; + } else { + e.Result = fileNames; + } + } + + // Callback that fires when a progress update is made. + private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { + int percent = e.ProgressPercentage; + string msg = e.UserState as string; + + Debug.Assert(percent >= 0 && percent <= 100); + + if (!string.IsNullOrEmpty(msg)) { + progressLabel.Text = msg; + } + progressBar1.Value = percent; + } + + // Callback that fires when execution completes. + private void backgroundWorker1_RunWorkerCompleted(object sender, + RunWorkerCompletedEventArgs e) { + if (e.Cancelled) { + Debug.WriteLine("CANCELED"); + DialogResult = DialogResult.Cancel; + } else if (e.Error != null) { + // This should only happen on a file I/O error, e.g. out of disk space or + // unable to overwrite an existing file. + MessageBox.Show(e.Error.ToString(), Properties.Resources.OPERATION_FAILED, + MessageBoxButtons.OK, MessageBoxIcon.Error); + DialogResult = DialogResult.Cancel; + } else { + // Make results available in property. + Results = e.Result as List; + + if (Results == null || Results.Count == 0) { + // Shouldn't happen -- generator should have reported error. + MessageBox.Show("Internal error: no files generated", + Properties.Resources.OPERATION_FAILED, + MessageBoxButtons.OK, MessageBoxIcon.Error); + } else { + Debug.WriteLine("SUCCESS " + Results.Count); + } + DialogResult = DialogResult.OK; + } + + // Whatever the case, we're done. + this.Close(); + } + } +} diff --git a/SourceGen/AsmGen/GeneratorProgress.resx b/SourceGen/AsmGen/GeneratorProgress.resx new file mode 100644 index 0000000..59099f2 --- /dev/null +++ b/SourceGen/AsmGen/GeneratorProgress.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/SourceGen/AsmGen/IAssembler.cs b/SourceGen/AsmGen/IAssembler.cs new file mode 100644 index 0000000..d884b8b --- /dev/null +++ b/SourceGen/AsmGen/IAssembler.cs @@ -0,0 +1,67 @@ +/* + * Copyright 2018 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; + +namespace SourceGen.AsmGen { + /// + /// Common interface for executing assemblers. + /// + public interface IAssembler { + /// + /// Queries the assembler for its version. + /// + /// Assembler version info, or null if query failed. + AssemblerVersion QueryVersion(); + + /// + /// Configures the object. Pass in the list of pathnames returned by IGenerator.Run(), + /// and the working directory to use for the shell command. + /// + /// Assembler source pathnames. + /// Working directory for shell command. + void Configure(List pathNames, string workDirectory); + + /// + /// Executes the assembler. Must call Configure() first. Executed on background thread. + /// + /// Async work object, used to report progress updates and + /// check for cancellation. + /// Execution results, or null on internal failure. + AssemblerResults RunAssembler(BackgroundWorker worker); + } + + /// + /// Set of values returned by the assembler. + /// + public class AssemblerResults { + public string CommandLine { get; private set; } + public int ExitCode { get; private set; } + public string Stdout { get; private set; } + public string Stderr { get; private set; } + public string OutputPathName { get; private set; } + + public AssemblerResults(string commandLine, int exitCode, string stdout, string stderr, + string outputFile) { + CommandLine = commandLine; + ExitCode = exitCode; + Stdout = stdout; + Stderr = stderr; + OutputPathName = outputFile; + } + } +} diff --git a/SourceGen/AsmGen/IGenerator.cs b/SourceGen/AsmGen/IGenerator.cs new file mode 100644 index 0000000..fc33913 --- /dev/null +++ b/SourceGen/AsmGen/IGenerator.cs @@ -0,0 +1,154 @@ +/* + * Copyright 2018 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 { + /// + /// Configure generator. Must be called before calling any other method or using + /// properties. + /// + /// 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; } + + LabelLocalizer Localizer { 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. + /// List of pathnames of generated files. + List GenerateSource(BackgroundWorker worker); + + /// + /// Provides an opportunity for the assembler to replace a mnemonic with another. This + /// is primarily intended for undocumented ops, which don't have standard mnemonics, + /// and hence can vary between assemblers. + /// + /// + /// + /// Replacement mnemonic, an empty string if the original is fine, or + /// null if the op is not supported at all and should be emitted as hex. + string ReplaceMnemonic(OpDef op); + + /// + /// Generates an opcode/operand pair for a short sequence of bytes (1-4 bytes). + /// Does not produce any source output. + /// + /// + /// + /// + /// + 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. + /// + /// + void OutputDataOp(int offset); + + /// + /// Outputs an equate directive. The numeric value is already formatted. + /// + /// + /// + /// + void OutputEquDirective(string name, string valueStr, string comment); + + /// + /// Outputs a code origin directive. + /// + /// + void OutputOrgDirective(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. + /// + /// + /// + /// + /// + /// + void OutputRegWidthDirective(int offset, int prevM, int prevX, int newM, int newX); + + /// + /// Output a line of source code. All elements must be fully formatted. The + /// items will be padded with spaces to fit specific column widths. + /// + /// + /// + /// + /// + void OutputLine(string label, string opcode, string operand, string comment); + + /// + /// Output a line of source code. + /// + /// + void OutputLine(string fullLine); + } + + public class AssemblerQuirks { + public bool BlockMoveArgsReversed { get; set; } + public bool TracksSepRepNotEmu { get; set; } + public bool NoPcRelBankWrap { get; set; } + } +} \ No newline at end of file diff --git a/SourceGen/AsmGen/LabelLocalizer.cs b/SourceGen/AsmGen/LabelLocalizer.cs new file mode 100644 index 0000000..ecbd32c --- /dev/null +++ b/SourceGen/AsmGen/LabelLocalizer.cs @@ -0,0 +1,313 @@ +/* + * Copyright 2018 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; +using System.Collections.Generic; +using System.Diagnostics; + +/* +Some assemblers support "local labels", with varying definitions of scope and features. +Generally speaking, local labels only need to be unique within a certain limited scope, and +they aren't included in end-of-assembly symbol lists. + +One popular form defines its scope as being between two global labels. So this is allowed: + + glob1 lda #$00 + :local sta $00 + glob2 lda #$01 + :local sta $01 + +but this would cause an error: + + glob1 lda #$00 + :local sta $00 + glob2 lda #$01 + bne :local + +because the local symbol table is cleared when a global symbol is encountered. + +Another common form allows backward references to labels that don't go out of scope until +they're re-used. This is useful for short loops. + +As a further limitation, assemblers seem to want the first label encountered in a program +to be global. + +The Symbol.SymbolType enum allows a label to be defined as "local or global". We can output +these with the local-symbol syntax, potentially rewriting them to have non-unique names like +"loop", but we can't promote (demote?) a label to local unless there are no references to it +that cross a global label. + +The cross-reference table we generate as part of the analysis process provides a full list of +label references, so we just need to iterate through the label list until we can't find +anything else that needs to be made global. + +Because the definition of "local label" is somewhat assembler-specific, it's best to defer +this analysis to code generation time, when the specific characteristics of the target +assembler can be taken into account. + +References to an offset can be numeric or symbolic. A purely numeric reference like "LDA $2000" +will always map to the offset associated with address $2000, but a symbolic reference might be +offset. For example, the LDA instruction could reference a label at $2008 as "LDA FOO-8". +The assembler cares about the symbolic references, not the actual offsets or addresses. For +this reason we can ignore references to an address with a label if those references don't +actually use the label. (One consequence of this is that formatting an operand as hex +eliminates it from the set of things for us to consider. Also, ORG directives have no effect +on the localizer.) + +Labels that are marked as global, but to which there are no references, could in theory be +elided. To do this we would have to omit them from the generated code, which would be +annoying and weird if (say) the user added them to label an external entry point. + + +The eventual output of our efforts is a map from the original symbol name to the local symbol +name. This must be applied to both labels and operands. +*/ + +namespace SourceGen.AsmGen { + public class LabelLocalizer { + /// + /// A pairing of an offset with a label string. (Essentially mAnattribs[n].Symbol + /// with all the fluff trimmed away.) + /// + /// The label string isn't actually all that useful, since we can pull it back out + /// of anattrib, but it makes life a little easier during debugging. These get + /// put into a List, so switching to a plain int offset doesn't necessarily help us + /// much because the ints get boxed. + /// + private class OffsetLabel { + public int Offset { get; private set; } + public string Label { get; private set; } + + public OffsetLabel(int offset, string label) { + Offset = offset; + Label = label; + } + + public override string ToString() { + return "+" + Offset.ToString("x6") + "(" + Label + ")"; + } + } + + /// + /// A pair of offsets. An operand (instruction or data) at the source offset + /// references a label at the destination offset. + /// + private class OffsetPair { + public int SrcOffset { get; private set; } // offset from which reference is made + public int DstOffset { get; private set; } // offset being referred to + + public OffsetPair(int src, int dst) { + SrcOffset = src; + DstOffset = dst; + } + + public override string ToString() { + return "src=+" + SrcOffset.ToString("x6") + " dst=+" + DstOffset.ToString("x6"); + } + } + + /// + /// Map from label string to local label string. This will be null until Analyze() + /// has executed. + /// + public Dictionary LabelMap { get; private set; } + + /// + /// String to prefix to local labels. + /// + public string LocalPrefix { get; set; } + + /// + /// Project reference. + /// + private DisasmProject mProject; + + // Work state. + private List mGlobalLabels = new List(); + private List mOffsetPairs = new List(); + private BitArray mGlobalFlags; + + + public LabelLocalizer(DisasmProject project) { + mProject = project; + mGlobalFlags = new BitArray(mProject.FileDataLength); + + LocalPrefix = "!?"; + } + + /// + /// Applies the LabelMap to the label. If the LabelMap is null, or does not have an + /// entry for the label, the original label is returned. + /// + /// Label to convert. + /// New label, or original label. + public string ConvLabel(string label) { + if (LabelMap != null) { + if (LabelMap.TryGetValue(label, out string newLabel)) { + label = newLabel; + } + } + return label; + } + + /// + /// Analyzes labels to identify which ones may be treated as non-global. + /// + public void Analyze() { + 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 + // mGlobalLabels list, and removed from the pair list. + for (int index = 0; index < mGlobalLabels.Count; index++) { + FindIntersectingPairs(mGlobalLabels[index]); + } + + // Step 3: for each local label, add an entry to the map with the appropriate + // local-label syntax. + LabelMap = new Dictionary(); + for (int i = 0; i < mProject.FileDataLength; i++) { + if (mGlobalFlags[i]) { + continue; + } + Symbol sym = mProject.GetAnattrib(i).Symbol; + if (sym == null) { + continue; + } + + LabelMap[sym.Label] = LocalPrefix + sym.Label; + } + + // Take out the trash. + mGlobalLabels.Clear(); + mOffsetPairs.Clear(); + } + + /// + /// Generates the initial mGlobalFlags and mGlobalLabels lists, as well as the + /// full cross-reference pair list. + /// + 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 i = 0; i < mProject.FileDataLength; i++) { + Symbol sym = mProject.GetAnattrib(i).Symbol; + if (sym == null) { + // No label at this offset. + continue; + } + + if (first || sym.SymbolType != Symbol.Type.LocalOrGlobalAddr) { + first = false; + mGlobalFlags[i] = true; + mGlobalLabels.Add(new OffsetLabel(i, 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(i); + if (xrefs != null) { + foreach (XrefSet.Xref xref in xrefs) { + if (!xref.IsSymbolic) { + continue; + } + + mOffsetPairs.Add(new OffsetPair(xref.Offset, i)); + } + } + } + } + + /// + /// Identifies all label reference pairs that cross the specified global label. When + /// a matching pair is found, the pair's destination label is marked as global and + /// added to the global label list. + /// + /// Global label of interest. + private void FindIntersectingPairs(OffsetLabel glabel) { + Debug.Assert(mGlobalFlags[glabel.Offset]); + + int globOffset = glabel.Offset; + for (int i = 0; i < mOffsetPairs.Count; i++) { + OffsetPair pair = mOffsetPairs[i]; + + // If the destination was marked global earlier, remove and ignore this entry. + // Note this also means that pair.DstOffset != label.Offset. + if (mGlobalFlags[pair.DstOffset]) { + mOffsetPairs.RemoveAt(i); + i--; + continue; + } + + // Check to see if the global label falls between the source and destination + // offsets. + // + // If the reference source is itself a global label, it can reference local + // labels forward, but not backward. We need to take that into account for + // the case where label.Offset==pair.SrcOffset. + bool intersect; + if (pair.SrcOffset < pair.DstOffset) { + // Forward reference. src==glob is ok + intersect = pair.SrcOffset < globOffset && pair.DstOffset >= globOffset; + } else { + // Backward reference. src==glob is bad + intersect = pair.SrcOffset >= globOffset && pair.DstOffset <= globOffset; + } + + if (intersect) { + //Debug.WriteLine("Global " + glabel + " btwn " + pair + " (" + + // mProject.GetAnattrib(pair.DstOffset).Symbol.Label + ")"); + + // Change the destination label to global. + mGlobalFlags[pair.DstOffset] = true; + mGlobalLabels.Add(new OffsetLabel(pair.DstOffset, + mProject.GetAnattrib(pair.DstOffset).Symbol.Label)); + + // Carefully remove it from the list we're iterating through. + mOffsetPairs.RemoveAt(i); + i--; + } + } + } + } +} diff --git a/SourceGen/AsmGen/StringGather.cs b/SourceGen/AsmGen/StringGather.cs new file mode 100644 index 0000000..798f349 --- /dev/null +++ b/SourceGen/AsmGen/StringGather.cs @@ -0,0 +1,227 @@ +/* + * Copyright 2018 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.Diagnostics; + +namespace SourceGen.AsmGen { + /// + /// Multi-line string gatherer. Accumulates characters and raw bytes, emitting + /// them when we have a full operand's worth. + /// + /// If the delimiter character appears, it will be output inline as a raw byte. + /// The low-ASCII string ['hello'world'] will become [27,'hello',27,'world',27] + /// (or something similar). + /// + public class StringGather { + // Inputs. + public IGenerator Gen { get; private set; } + public string Label { get; private set; } + public string Opcode { get; private set; } + public string Comment { get; private set; } + public char Delimiter { get; private set; } + public char DelimiterReplacement { get; private set; } + public ByteStyle ByteStyleX { get; private set; } + public int MaxOperandLen { get; private set; } + public bool IsTestRun { get; private set; } + + public enum ByteStyle { DenseHex, CommaSep }; + + // Outputs. + public bool HasDelimiter { get; private set; } + public int NumLinesOutput { get; private set; } + + private char[] mHexChars; + + /// + /// Character collection buffer. The delimiters are written into the buffer + /// because they're mixed with bytes, particularly when we have to escape the + /// delimiter character. Strings might start or end with escaped delimiters, + /// so we don't add them until we have to. + private char[] mBuffer; + + /// + /// Next available character position. + /// + private int mIndex = 0; + + /// + /// State of the buffer, based on the last thing we added. + /// + private enum State { + Unknown = 0, + StartOfLine, + InQuote, + OutQuote + } + private State mState = State.StartOfLine; + + /// + /// Constructor. + /// + /// Reference back to generator, for output function and + /// format options. + /// Line label. Appears on first output line only. + /// Opcode to use for all lines. + /// End-of-line comment. Appears on first output line + /// only. + /// String delimiter character. + /// If true, no file output is produced. + public StringGather(IGenerator gen, string label, string opcode, + string comment, char delimiter, char delimReplace, ByteStyle byteStyle, + int maxOperandLen, bool isTestRun) { + Gen = gen; + Label = label; + Opcode = opcode; + Comment = comment; + Delimiter = delimiter; + DelimiterReplacement = delimReplace; + ByteStyleX = byteStyle; + MaxOperandLen = maxOperandLen; + IsTestRun = isTestRun; + + mBuffer = new char[MaxOperandLen]; + mHexChars = Gen.SourceFormatter.HexDigits; + } + + /// + /// Write a character into the buffer. + /// + /// Character to add. + public void WriteChar(char ch) { + Debug.Assert(ch >= 0 && ch <= 0xff); + if (ch == Delimiter) { + // Must write it as a byte. + HasDelimiter = true; + WriteByte((byte)DelimiterReplacement); + return; + } + + // If we're at the start of a line, add delimiter, then new char. + // If we're inside quotes, just add the character. We must have space for + // two chars (new char, close quote). + // If we're outside quotes, add a comma and delimiter, then the character. + // We must have 4 chars remaining (comma, open quote, new char, close quote). + switch (mState) { + case State.StartOfLine: + mBuffer[mIndex++] = Delimiter; + break; + case State.InQuote: + if (mIndex + 2 > MaxOperandLen) { + Flush(); + mBuffer[mIndex++] = Delimiter; + } + break; + case State.OutQuote: + if (mIndex + 4 > MaxOperandLen) { + Flush(); + mBuffer[mIndex++] = Delimiter; + } else { + mBuffer[mIndex++] = ','; + mBuffer[mIndex++] = Delimiter; + } + break; + default: + Debug.Assert(false); + break; + } + mBuffer[mIndex++] = ch; + mState = State.InQuote; + } + + /// + /// Write a hex value into the buffer. + /// + /// Value to add. + public void WriteByte(byte val) { + // If we're at the start of a line, just output the byte. + // If we're inside quotes, emit a delimiter, comma, and the byte. We must + // have space for four (DenseHex) or five (CommaSep) chars. + // If we're outside quotes, add the byte. We must have two (DenseHex) or + // four (CommaSep) chars remaining. + switch (mState) { + case State.StartOfLine: + break; + case State.InQuote: + int minWidth = (ByteStyleX == ByteStyle.CommaSep) ? 5 : 4; + if (mIndex + minWidth > MaxOperandLen) { + Flush(); + } else { + mBuffer[mIndex++] = Delimiter; + mBuffer[mIndex++] = ','; + } + break; + case State.OutQuote: + minWidth = (ByteStyleX == ByteStyle.CommaSep) ? 4 : 2; + if (mIndex + minWidth > MaxOperandLen) { + Flush(); + } else { + if (ByteStyleX == ByteStyle.CommaSep) { + mBuffer[mIndex++] = ','; + } + } + break; + default: + Debug.Assert(false); + break; + } + + if (ByteStyleX == ByteStyle.CommaSep) { + mBuffer[mIndex++] = '$'; + } + mBuffer[mIndex++] = mHexChars[val >> 4]; + mBuffer[mIndex++] = mHexChars[val & 0x0f]; + mState = State.OutQuote; + } + + /// + /// Tells the object to flush any pending data to the output. + /// + public void Finish() { + Flush(); + } + + /// + /// Outputs the buffer of pending data. A closing delimiter will be added if needed. + /// + private void Flush() { + switch (mState) { + case State.StartOfLine: + // empty string; put out a pair of delimiters + mBuffer[mIndex++] = Delimiter; + mBuffer[mIndex++] = Delimiter; + NumLinesOutput++; + break; + case State.InQuote: + // add delimiter and finish + mBuffer[mIndex++] = Delimiter; + NumLinesOutput++; + break; + case State.OutQuote: + // just output it + NumLinesOutput++; + break; + } + if (!IsTestRun) { + Gen.OutputLine(Label, Opcode, new string(mBuffer, 0, mIndex), + Comment); + } + mIndex = 0; + + // Erase these after first use so we don't put them on every line. + Label = Comment = string.Empty; + } + } +} diff --git a/SourceGen/ChangeSet.cs b/SourceGen/ChangeSet.cs new file mode 100644 index 0000000..4e953f9 --- /dev/null +++ b/SourceGen/ChangeSet.cs @@ -0,0 +1,109 @@ +/* + * Copyright 2018 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; +using System.Collections.Generic; +using System.Diagnostics; + +namespace SourceGen { + /// + /// Holds information about a set of changes. + /// + /// Does not have hooks into other data structures. This just holds the information + /// about the changes. + /// + public class ChangeSet : IEnumerable { + private List mChanges; + + /// + /// Constructs an empty ChangeSet with the specified initial capacity. + /// + /// Initial number of elements that the set can contain. + public ChangeSet(int capacity) { + mChanges = new List(capacity); + } + + /// + /// Constructs a ChangeSet with a single change. + /// + public ChangeSet(UndoableChange ac) { + mChanges = new List(1); + mChanges.Add(ac); + } + + /// + /// The number of changes in the set. + /// + public int Count { get { return mChanges.Count; } } + + /// + /// Returns the Nth change in the set. + /// + /// Change index. + public UndoableChange this[int key] { + get { + return mChanges[key]; + } + } + + /// + /// Adds a change to the change set. + /// + /// Change to add. + public void Add(UndoableChange change) { + Debug.Assert(change != null); + mChanges.Add(change); + } + + /// + /// Adds a change to the change set if the object is non-null. + /// + /// Change to add, or null. + public void AddNonNull(UndoableChange change) { + if (change != null) { + Add(change); + } + } + + /// + /// Trims unused capacity from the set. + /// + public void TrimExcess() { + mChanges.TrimExcess(); + } + + // IEnumerable, so we can use foreach syntax when going forward + public IEnumerator GetEnumerator() { + return mChanges.GetEnumerator(); + } + + // IEnumerable: generic version + IEnumerator IEnumerable.GetEnumerator() { + return mChanges.GetEnumerator(); + } + + // TODO(maybe): reverse-order enumerator? + + public override string ToString() { + string str = "[CS: count=" + mChanges.Count; + if (mChanges.Count > 0) { + str += " {0:" + mChanges[0] + "}"; + } + str += "]"; + return str; + } + } +} diff --git a/SourceGen/CodeAnalysis.cs b/SourceGen/CodeAnalysis.cs new file mode 100644 index 0000000..df2845f --- /dev/null +++ b/SourceGen/CodeAnalysis.cs @@ -0,0 +1,1070 @@ +/* + * Copyright 2018 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.Diagnostics; + +using Asm65; +using CommonUtil; +using PluginCommon; +using SourceGen.Sandbox; + +namespace SourceGen { + /// + /// Instruction analyzer. + /// + /// All data held in this object is transient, and will be discarded when analysis + /// completes. All user-defined values should be held elsewhere and provided as inputs + /// to the analyzer. Any change that merits re-analysis should be handled by creating a + /// new instance of this object. + /// + /// See the comments at the top of UndoableChange for a list of things that can + /// mandate code re-analysis. + /// + public class CodeAnalysis { + /// + /// Type hints are specified by the user. The identify a region as being code + /// or data. The code analyzer will stop at data-hinted regions, and will + /// process any code-hinted regions during the dead-code pass. + /// + /// The hints are not used directly by the data analyzer, but the effects they + /// have on the Anattrib array are. + /// + public enum TypeHint : sbyte { + // No hint. Default value populated in new arrays. + NoHint = 0, + + // Byte is an instruction. If the code analyzer doesn't find this + // naturally, it will be scanned. + Code, + + // Byte is inline data. Execution continues "through" the byte. + InlineData, + + // Byte is data. Execution halts. + Data + } + + /// + /// Class for handling callbacks from extension scripts. + /// + private class ScriptSupport : MarshalByRefObject, PluginCommon.IApplication { + private CodeAnalysis mOuter; + + public ScriptSupport(CodeAnalysis ca) { + mOuter = ca; + } + + /// + /// Call this when analysis is complete, to ensure that over-active scripts + /// can't keep doing things. (This is not part of IApplication.) + /// + public void Shutdown() { + mOuter = null; + } + + public void DebugLog(string msg) { + mOuter.mDebugLog.LogI("PLUGIN: " + msg); + } + + public bool SetOperandFormat(int offset, DataSubType subType, string label) { + return mOuter.SetOperandFormat(offset, subType, label); + } + + public bool SetInlineDataFormat(int offset, int length, DataType type, + DataSubType subType, string label) { + return mOuter.SetInlineDataFormat(offset, length, type, subType, label); + } + } + + /// + /// Extension script manager. + /// + private ScriptManager mScriptManager; + + /// + /// Local object that implements the IApplication interface for plugins. + /// + private ScriptSupport mScriptSupport; + + /// + /// List of interesting plugins. If we have plugins that don't do code inlining we + /// can ignore them. (I'm using an array instead of a List<IPlugin> as a + /// micro-optimization; see https://stackoverflow.com/a/454923/294248 .) + /// + private IPlugin[] mScriptArray; + + /// + /// CPU to use when analyzing data. + /// + private CpuDef mCpuDef; + + /// + /// Map of offsets to addresses. + /// + private AddressMap mAddrMap; + + /// + /// Reference to 65xx data. + /// + private byte[] mFileData; + + /// + /// Attributes, one per byte in input file. + /// + private Anattrib[] mAnattribs; + + /// + /// Reference to type hint array, one hint per byte. + /// + private TypeHint[] mTypeHints; + + /// + /// Reference to status flag override array, one entry per byte. + /// + private StatusFlags[] mStatusFlagOverrides; + + /// + /// Initial status flags to use at entry points. + /// + private StatusFlags mEntryFlags; + + /// + /// Debug trace log. + /// + private DebugLog mDebugLog = new DebugLog(DebugLog.Priority.Silent); + + + /// + /// Constructor. + /// + /// 65xx code stream. + /// CPU definition to use when interpreting code. + /// Anattrib array. Expected to be newly allocated, all + /// entries set to default values. + /// Map of offsets to addresses. + /// Type hints, one per byte. + /// Status flag overrides for instruction-start + /// bytes. + /// Status flags to use at code entry points. + /// Extension script manager. + /// Object that receives debug log messages. + public CodeAnalysis(byte[] data, CpuDef cpuDef, Anattrib[] anattribs, + AddressMap addrMap, TypeHint[] hints, StatusFlags[] statusFlagOverrides, + StatusFlags entryFlags, ScriptManager scriptMan, DebugLog debugLog) { + mFileData = data; + mCpuDef = cpuDef; + mAnattribs = anattribs; + mAddrMap = addrMap; + mTypeHints = hints; + mStatusFlagOverrides = statusFlagOverrides; + mEntryFlags = entryFlags; + mScriptManager = scriptMan; + mDebugLog = debugLog; + + mScriptSupport = new ScriptSupport(this); + } + + // Internal log functions. If we're concerned about performance overhead due to + // call-site string concatenation, we can #ifdef these to nothing in release builds, + // which should allow the compiler to elide the concat. +#if false + private void LogV(int offset, string msg) { + if (mDebugLog.IsLoggable(DebugLog.Priority.Verbose)) { + mDebugLog.LogV("+" + offset.ToString("x6") + " " + msg); + } + } +#else + private void LogV(int offset, string msg) { } +#endif +#if true + private void LogD(int offset, string msg) { + if (mDebugLog.IsLoggable(DebugLog.Priority.Debug)) { + mDebugLog.LogD("+" + offset.ToString("x6") + " " + msg); + } + } + private void LogI(int offset, string msg) { + if (mDebugLog.IsLoggable(DebugLog.Priority.Info)) { + mDebugLog.LogI("+" + offset.ToString("x6") + " " + msg); + } + } + private void LogW(int offset, string msg) { + if (mDebugLog.IsLoggable(DebugLog.Priority.Warning)) { + mDebugLog.LogW("+" + offset.ToString("x6") + " " + msg); + } + } + private void LogE(int offset, string msg) { + if (mDebugLog.IsLoggable(DebugLog.Priority.Error)) { + mDebugLog.LogE("+" + offset.ToString("x6") + " " + msg); + } + } +#else + private void LogD(int offset, string msg) { } + private void LogI(int offset, string msg) { } + private void LogW(int offset, string msg) { } + private void LogE(int offset, string msg) { } +#endif + + /// + /// Analyze a blob of code and data, annotating all code areas. + /// + /// Also identifies data embedded in code, e.g. parameter blocks following a JSR, + /// with the help of extension scripts. + /// + /// Failing here can leave us in a strange state, so prefer to work around unexpected + /// inputs rather than bailing entirely. + /// + public void Analyze() { + List scanOffsets = new List(); + + mDebugLog.LogI("Analyzing code: " + mFileData.Length + " bytes, CPU=" + mCpuDef.Name); + + PrepareScripts(); + + SetAddresses(); + + // Set the "is data" and "is inline data" flags on anything that the user has + // flagged as being such. This tells us to stop processing or skip over bytes + // as we work. We don't need to flag code hints explicitly for analysis, but + // we want to be able to display the flags in the info window. + // + // The data recognizers may spot additional inline data offsets as we work. This + // can cause a race if it mis-identifies code that is also a branch target; + // whichever marks the code first will win. + UnpackTypeHints(); + + // Find starting place, based on type hints. + // We only set the "visited" flag on the instruction start, so if the user + // puts a code hint in the middle of an instruction, we will find it and + // treat it as an entry point. (This is useful for embedded instructions + // that are branched to by code we aren't able to detect.) + int searchStart = FindFirstUnvisitedInstruction(0); + while (searchStart >= 0) { + mAnattribs[searchStart].IsEntryPoint = true; + mAnattribs[searchStart].StatusFlags = mEntryFlags; + mAnattribs[searchStart].ApplyStatusFlags(mStatusFlagOverrides[searchStart]); + + int offset = searchStart; + while (true) { + bool embedded = (mAnattribs[offset].IsInstruction && + !mAnattribs[offset].IsVisited); + LogI(offset, "Scan chunk (vis=" + mAnattribs[offset].IsVisited + + " chg=" + mAnattribs[offset].IsChanged + + (embedded ? " embedded " : "") + ")"); + + AnalyzeSegment(offset, scanOffsets); + + // Did anything new get added? + if (scanOffsets.Count == 0) { + break; + } + + // Pop one off the end. + int lastItem = scanOffsets.Count - 1; + offset = scanOffsets[lastItem]; + scanOffsets.RemoveAt(lastItem); + } + + searchStart = FindFirstUnvisitedInstruction(searchStart); + } + + mScriptSupport.Shutdown(); + + MarkUnexecutedEmbeddedCode(); + } + + /// + /// Prepare a list of relevant extension scripts. + /// + private void PrepareScripts() { + if (mScriptManager == null) { + // Currently happens for regression tests with no external files. + mScriptArray = new IPlugin[0]; + return; + } + + // Include all scripts. + mScriptArray = mScriptManager.GetAllInstances().ToArray(); + + // Prep them. + mScriptManager.PrepareScripts(mScriptSupport); + } + + /// + /// Sets the address for every byte in the input. + /// + private void SetAddresses() { + // The AddressMap will have at least one entry, will start at offset 0, and + // will exactly span the file. + foreach (AddressMap.AddressMapEntry ent in mAddrMap) { + int addr = ent.Addr; + for (int i = ent.Offset; i < ent.Offset + ent.Length; i++) { + mAnattribs[i].Address = addr++; + } + } + } + + /// + /// Sets the "is xxxxx" flags on type-hinted entries, so that the code analyzer + /// can find them easily. + /// + private void UnpackTypeHints() { + Debug.Assert(mTypeHints.Length == mAnattribs.Length); + int offset = 0; + foreach (TypeHint hint in mTypeHints) { + switch (hint) { + case TypeHint.Code: + // Set the IsInstruction flag to prevent inline data from being + // placed here. + OpDef op = mCpuDef.GetOpDef(mFileData[offset]); + if (op == OpDef.OpInvalid) { + LogI(offset, "Ignoring code hint on illegal opcode"); + } else { + mAnattribs[offset].IsHinted = true; + mAnattribs[offset].IsInstruction = true; + } + break; + case TypeHint.Data: + // Tells the code analyzer to stop. Does not define a data analyzer + // "uncategorized data" boundary. + mAnattribs[offset].IsHinted = true; + mAnattribs[offset].IsData = true; + break; + case TypeHint.InlineData: + // Tells the code analyzer to walk across these. + mAnattribs[offset].IsHinted = true; + mAnattribs[offset].IsInlineData = true; + break; + case TypeHint.NoHint: + break; + default: + Debug.Assert(false); + break; + } + offset++; + } + } + + /// + /// Finds the first offset that is hinted as code but hasn't yet been visited. + /// + /// This might be in the middle of an already-visited instruction. + /// + /// Offset at which to start the search. + /// Offset found. + private int FindFirstUnvisitedInstruction(int start) { + for (int i = start; i < mAnattribs.Length; i++) { + if (mAnattribs[i].IsHinted && mTypeHints[i] == TypeHint.Code && + !mAnattribs[i].IsVisited) { + LogD(i, "Unvisited code hint"); + if (mAnattribs[i].IsData || mAnattribs[i].IsInlineData) { + // Maybe the user put a code hint on something that was + // later recognized as inline data? Shouldn't have been allowed. + LogW(i, "Weird: code hint on data/inline"); + continue; + } + return i; + } + } + return -1; + } + + /// + /// Finds bits of code that are part of embedded instructions but not actually + /// executed, and marks them as inline data. + /// + private void MarkUnexecutedEmbeddedCode() { + // The problem arises when you have a line like 4C 60 EA, with a branch to the + // middle byte. The formatter will print "JMP $EA60", then "