mirror of
https://github.com/fadden/6502bench.git
synced 2024-12-03 05:49:48 +00:00
Binary includes
This adds a new data format option, "binary include", that takes a filename operand. When assembly sources are generated, the section of file is replaced with an appropriate pseudo-op, and binary files are generated that hold the file contents. This is a convenient way to remove large binary blobs, such as music or sound samples, that aren't useful to have in text form in the sources. Partial pathnames are allowed, so you can output a sound blob to "sounds/blather.bin". For safety reasons, we don't allow the files to be created above the project directory, and existing files will only be overwritten if they have a matching length (so you don't accidentally stomp on your project file). The files are not currently shown in the GenAsm dialog, which lets you see a preview of the generated sources. The hex dump tool can do this for the (presumably rare) situations where it's useful. A new regression test, 20300-binary-include, has been added. The pseudo-op name can be overridden on-screen in the settings. We don't currently do anything new for text/HTML exports. It might be useful to generate an optional appendix with a hex dump of the excised sections. (issue #144)
This commit is contained in:
parent
fa5d9ba890
commit
4e5c34f457
@ -454,7 +454,8 @@ namespace PluginCommon {
|
||||
Dense,
|
||||
Fill,
|
||||
Uninit,
|
||||
Junk
|
||||
Junk,
|
||||
BinaryInclude
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -56,6 +56,11 @@ namespace SourceGen.AsmGen {
|
||||
// IGenerator
|
||||
public int StartOffset { get { return 0; } }
|
||||
|
||||
/// <summary>
|
||||
/// List of binary include sections found in the project.
|
||||
/// </summary>
|
||||
private List<BinaryInclude.Excision> mBinaryIncludes = new List<BinaryInclude.Excision>();
|
||||
|
||||
/// <summary>
|
||||
/// Working directory, i.e. where we write our output file(s).
|
||||
/// </summary>
|
||||
@ -139,6 +144,7 @@ namespace SourceGen.AsmGen {
|
||||
{ "Uninit", "!skip" },
|
||||
//Junk
|
||||
{ "Align", "!align" },
|
||||
{ "BinaryInclude", "!binary" },
|
||||
{ "StrGeneric", "!text" }, // can use !xor for high ASCII
|
||||
//StrReverse
|
||||
//StrNullTerm
|
||||
@ -303,7 +309,7 @@ namespace SourceGen.AsmGen {
|
||||
}
|
||||
mOutStream = null;
|
||||
|
||||
return new GenerationResults(pathNames, string.Empty);
|
||||
return new GenerationResults(pathNames, string.Empty, mBinaryIncludes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -483,6 +489,12 @@ namespace SourceGen.AsmGen {
|
||||
OutputDenseHex(offset, length, labelStr, commentStr);
|
||||
}
|
||||
break;
|
||||
case FormatDescriptor.Type.BinaryInclude:
|
||||
opcodeStr = sDataOpNames.BinaryInclude;
|
||||
string biPath = BinaryInclude.ConvertPathNameFromStorage(dfd.Extra);
|
||||
operandStr = '"' + biPath + '"';
|
||||
mBinaryIncludes.Add(new BinaryInclude.Excision(offset, length, biPath));
|
||||
break;
|
||||
case FormatDescriptor.Type.StringGeneric:
|
||||
case FormatDescriptor.Type.StringReverse:
|
||||
case FormatDescriptor.Type.StringNullTerm:
|
||||
|
@ -51,6 +51,11 @@ namespace SourceGen.AsmGen {
|
||||
// IGenerator
|
||||
public int StartOffset { get { return 0; } }
|
||||
|
||||
/// <summary>
|
||||
/// List of binary include sections found in the project.
|
||||
/// </summary>
|
||||
private List<BinaryInclude.Excision> mBinaryIncludes = new List<BinaryInclude.Excision>();
|
||||
|
||||
/// <summary>
|
||||
/// Working directory, i.e. where we write our output file(s).
|
||||
/// </summary>
|
||||
@ -138,6 +143,8 @@ namespace SourceGen.AsmGen {
|
||||
{ "Dense", ".byte" }, // really just just comma-separated bytes
|
||||
{ "Uninit", ".res" },
|
||||
//Junk
|
||||
//Align
|
||||
{ "BinaryInclude", ".incbin" },
|
||||
{ "StrGeneric", ".byte" },
|
||||
//StrReverse
|
||||
{ "StrNullTerm", ".asciiz" },
|
||||
@ -262,7 +269,7 @@ namespace SourceGen.AsmGen {
|
||||
}
|
||||
mOutStream = null;
|
||||
|
||||
return new GenerationResults(pathNames, string.Empty);
|
||||
return new GenerationResults(pathNames, string.Empty, mBinaryIncludes);
|
||||
}
|
||||
|
||||
private void GenerateLinkerScript(StreamWriter sw) {
|
||||
@ -470,6 +477,12 @@ namespace SourceGen.AsmGen {
|
||||
OutputDenseHex(offset, length, labelStr, commentStr);
|
||||
}
|
||||
break;
|
||||
case FormatDescriptor.Type.BinaryInclude:
|
||||
opcodeStr = sDataOpNames.BinaryInclude;
|
||||
string biPath = BinaryInclude.ConvertPathNameFromStorage(dfd.Extra);
|
||||
operandStr = '"' + biPath + '"';
|
||||
mBinaryIncludes.Add(new BinaryInclude.Excision(offset, length, biPath));
|
||||
break;
|
||||
case FormatDescriptor.Type.StringGeneric:
|
||||
case FormatDescriptor.Type.StringReverse:
|
||||
case FormatDescriptor.Type.StringNullTerm:
|
||||
|
@ -133,6 +133,7 @@ namespace SourceGen.AsmGen {
|
||||
{ "Uninit", "ds" },
|
||||
//Junk
|
||||
//Align
|
||||
//BinaryInclude
|
||||
{ "StrGeneric", "asc" },
|
||||
{ "StrReverse", "rev" },
|
||||
//StrNullTerm
|
||||
@ -243,7 +244,8 @@ namespace SourceGen.AsmGen {
|
||||
}
|
||||
mOutStream = null;
|
||||
|
||||
return new GenerationResults(pathNames, string.Empty);
|
||||
return new GenerationResults(pathNames, string.Empty,
|
||||
new List<BinaryInclude.Excision>());
|
||||
}
|
||||
|
||||
// IGenerator
|
||||
@ -316,6 +318,7 @@ namespace SourceGen.AsmGen {
|
||||
break;
|
||||
case FormatDescriptor.Type.Uninit:
|
||||
case FormatDescriptor.Type.Junk:
|
||||
case FormatDescriptor.Type.BinaryInclude: // not supported, gen minimal output
|
||||
int fillVal = Helper.CheckRangeHoldsSingleValue(data, offset, length);
|
||||
if (fillVal >= 0) {
|
||||
opcodeStr = sDataOpNames.Fill;
|
||||
|
@ -61,12 +61,18 @@ namespace SourceGen.AsmGen {
|
||||
// IGenerator
|
||||
public LabelLocalizer Localizer { get { return mLocalizer; } }
|
||||
|
||||
// IGenerator
|
||||
public int StartOffset {
|
||||
get {
|
||||
return mHasPrgHeader ? 2 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of binary include sections found in the project.
|
||||
/// </summary>
|
||||
private List<BinaryInclude.Excision> mBinaryIncludes = new List<BinaryInclude.Excision>();
|
||||
|
||||
/// <summary>
|
||||
/// Working directory, i.e. where we write our output file(s).
|
||||
/// </summary>
|
||||
@ -158,6 +164,7 @@ namespace SourceGen.AsmGen {
|
||||
{ "Uninit", ".fill" },
|
||||
//Junk
|
||||
{ "Align", ".align" },
|
||||
{ "BinaryInclude", ".binary" },
|
||||
{ "StrGeneric", ".text" },
|
||||
//StrReverse
|
||||
{ "StrNullTerm", ".null" },
|
||||
@ -323,7 +330,7 @@ namespace SourceGen.AsmGen {
|
||||
}
|
||||
mOutStream = null;
|
||||
|
||||
return new GenerationResults(pathNames, extraOptions);
|
||||
return new GenerationResults(pathNames, extraOptions, mBinaryIncludes);
|
||||
}
|
||||
|
||||
// IGenerator
|
||||
@ -577,6 +584,12 @@ namespace SourceGen.AsmGen {
|
||||
OutputDenseHex(offset, length, labelStr, commentStr);
|
||||
}
|
||||
break;
|
||||
case FormatDescriptor.Type.BinaryInclude:
|
||||
opcodeStr = sDataOpNames.BinaryInclude;
|
||||
string biPath = BinaryInclude.ConvertPathNameFromStorage(dfd.Extra);
|
||||
operandStr = '"' + biPath + '"';
|
||||
mBinaryIncludes.Add(new BinaryInclude.Excision(offset, length, biPath));
|
||||
break;
|
||||
case FormatDescriptor.Type.StringGeneric:
|
||||
case FormatDescriptor.Type.StringReverse:
|
||||
case FormatDescriptor.Type.StringNullTerm:
|
||||
|
225
SourceGen/AsmGen/BinaryInclude.cs
Normal file
225
SourceGen/AsmGen/BinaryInclude.cs
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright 2024 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;
|
||||
|
||||
namespace SourceGen.AsmGen {
|
||||
/// <summary>
|
||||
/// Helper functions for working with binary includes.
|
||||
/// </summary>
|
||||
public static class BinaryInclude {
|
||||
// Character placed at the start of a path as a check that the field holds what we
|
||||
// expect. If we want to modify the structure of the string, e.g. to add or remove
|
||||
// additional fields, we can change the character to something else.
|
||||
private static char PATH_PREFIX_CHAR = '\u2191'; // UPWARDS ARROW
|
||||
|
||||
/// <summary>
|
||||
/// Class to help when gathering up binary includes during asm gen.
|
||||
/// </summary>
|
||||
public class Excision {
|
||||
// Offset of start of region to excise.
|
||||
public int Offset { get; private set; }
|
||||
|
||||
// Length of region to excise.
|
||||
public int Length { get; private set; }
|
||||
|
||||
// Partial pathname of output file, as stored in the project.
|
||||
public string PathName { get; private set; }
|
||||
|
||||
// Full output file path, initially null, set by PrepareList().
|
||||
public string FullPath { get; set; }
|
||||
|
||||
public Excision(int offset, int length, string pathName) {
|
||||
Offset = offset;
|
||||
Length = length;
|
||||
PathName = pathName;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return "[Exc: offset=+" + Offset.ToString("x6") + " len=" + Length +
|
||||
"path=\"" + PathName + "\" fullPath=\"" + FullPath + "\"]";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the full path of each binary include output file. Checks for duplicates.
|
||||
/// Sorts the list by case-insensitive pathname.
|
||||
/// </summary>
|
||||
/// <param name="list">List of binary include excisions.</param>
|
||||
/// <param name="workDir">Working directory.</param>
|
||||
/// <param name="failMsg">On failure, a human-readable error message.</param>
|
||||
/// <returns>True on success.</returns>
|
||||
public static bool PrepareList(List<Excision> list, string workDir, out string failMsg) {
|
||||
// Normalize the pathname. This is not expected to fail.
|
||||
string fullWorkDir = Path.GetFullPath(workDir);
|
||||
|
||||
string oldCurrentDir = Environment.CurrentDirectory;
|
||||
try {
|
||||
Environment.CurrentDirectory = workDir;
|
||||
|
||||
foreach (Excision exc in list) {
|
||||
try {
|
||||
exc.FullPath = Path.GetFullPath(exc.PathName);
|
||||
} catch (Exception ex) {
|
||||
failMsg = "unable to get full path for binary include \"" +
|
||||
exc.PathName + "\": " + ex.Message;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!exc.FullPath.StartsWith(fullWorkDir)) {
|
||||
failMsg = "binary include path for \"" + exc.PathName +
|
||||
"\" resolved to parent directory";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Environment.CurrentDirectory = oldCurrentDir;
|
||||
}
|
||||
|
||||
// Check for duplicates. Assume filenames are case-insensitive.
|
||||
list.Sort(delegate (Excision a, Excision b) {
|
||||
return string.Compare(a.PathName, b.PathName,
|
||||
StringComparison.InvariantCultureIgnoreCase);
|
||||
});
|
||||
string prev = null;
|
||||
foreach (Excision exc in list) {
|
||||
if (prev != null && exc.FullPath == prev) {
|
||||
failMsg = "found multiple binary includes that output to \"" + prev + "\"";
|
||||
return false;
|
||||
}
|
||||
prev = exc.FullPath;
|
||||
}
|
||||
|
||||
failMsg = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the output file with the binary include data.
|
||||
/// </summary>
|
||||
/// <param name="exc">Binary include object, with full pathname computed.</param>
|
||||
/// <param name="data">Project data array.</param>
|
||||
/// <param name="failMsg">On failure, a human-readable error message.</param>
|
||||
/// <returns>True on success.</returns>
|
||||
public static bool GenerateOutputFile(Excision exc, byte[] data, out string failMsg) {
|
||||
if (exc.FullPath == null) {
|
||||
failMsg = "internal error";
|
||||
return false;
|
||||
}
|
||||
if (File.Exists(exc.FullPath)) {
|
||||
// Test the file length. If it's different, don't overwrite the existing file.
|
||||
// Make an exception if it's zero bytes long?
|
||||
long fileLen = new FileInfo(exc.FullPath).Length;
|
||||
if (exc.Length != fileLen) {
|
||||
failMsg = "output file \"" + exc.PathName + "\" exists and " +
|
||||
"has a different length (" + fileLen + " vs. " + exc.Length + ")";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
// Create any directories in the path.
|
||||
string dirName = Path.GetDirectoryName(exc.FullPath);
|
||||
Directory.CreateDirectory(dirName);
|
||||
// Create the file and copy the data into it.
|
||||
Debug.Assert(exc.Offset < data.Length && exc.Offset + exc.Length <= data.Length);
|
||||
using (Stream stream = new FileStream(exc.FullPath, FileMode.OpenOrCreate,
|
||||
FileAccess.ReadWrite, FileShare.None)) {
|
||||
stream.SetLength(0);
|
||||
stream.Write(data, exc.Offset, exc.Length);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
failMsg = "unable to create '" + exc.PathName + "': " + ex.Message;
|
||||
return false;
|
||||
}
|
||||
failMsg = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates a binary-include filename. We allow partial paths, but they're not allowed
|
||||
/// to ascend above the current directory. Does not access the filesystem.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The Path.GetFullPath() call hits the filesystem, which is undesirable for
|
||||
/// a check-as-you-type test. We just want to avoid having a "rooted" path or something
|
||||
/// with a ".." directory reference.</para>
|
||||
/// <para>This is intended as a simple measure to avoid having important files
|
||||
/// overwritten by an asm generation command. The file generator could employ other
|
||||
/// measures, e.g. checking to see if an existing output file has the same size. (Note
|
||||
/// some malicious individual could hand-edit the filename in the project file.)</para>
|
||||
/// <para>We screen the filename for illegal characters, though what works on one
|
||||
/// platform might not on another. We can't guarantee validity.</para>
|
||||
/// </remarks>
|
||||
/// <param name="pathName">Partial path to verify.</param>
|
||||
/// <returns>True if the path looks correct.</returns>
|
||||
public static bool ValidatePathName(string pathName) {
|
||||
if (string.IsNullOrEmpty(pathName)) {
|
||||
return false;
|
||||
}
|
||||
// In .NET Framework, IsPathRooted() will throw if invalid chars are found. This is
|
||||
// not a full syntax check, just a char test. The behavior changed in .NET Core 2.1.
|
||||
try {
|
||||
if (Path.IsPathRooted(pathName)) {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Debug.WriteLine("GetFileName rejected pathname: " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to screen out "../foo", "x/../y", "bar/..", without rejecting "..my..stuff..".
|
||||
// Normalize to forward-slash and split into components.
|
||||
string normal = pathName.Replace('\\', '/');
|
||||
string[] parts = normal.Split('/');
|
||||
foreach (string part in parts) {
|
||||
if ("..".Equals(part)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Reject names with a double quote, so we don't have to figure out the quote-quoting
|
||||
// mechanism for every assembler.
|
||||
if (normal.Contains("\"")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a binary include pathname to a format suited for storage.
|
||||
/// </summary>
|
||||
/// <param name="pathName">Partial pathname.</param>
|
||||
/// <returns>String to store.</returns>
|
||||
public static string ConvertPathNameToStorage(string pathName) {
|
||||
return PATH_PREFIX_CHAR + pathName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the stored name back to a path prefix string.
|
||||
/// </summary>
|
||||
/// <param name="storageStr">Stored string.</param>
|
||||
/// <returns>Path prefix.</returns>
|
||||
public static string ConvertPathNameFromStorage(string storageStr) {
|
||||
if (string.IsNullOrEmpty(storageStr) || storageStr[0] != PATH_PREFIX_CHAR) {
|
||||
return "!BAD STORED NAME!";
|
||||
}
|
||||
return storageStr.Substring(1);
|
||||
}
|
||||
}
|
||||
}
|
@ -288,10 +288,13 @@ namespace SourceGen.AsmGen {
|
||||
public class GenerationResults {
|
||||
public List<string> PathNames { get; private set; }
|
||||
public string ExtraOptions { get; private set; }
|
||||
public List<BinaryInclude.Excision> BinaryIncludes { get; private set; }
|
||||
|
||||
public GenerationResults(List<string> pathNames, string extraOptions) {
|
||||
public GenerationResults(List<string> pathNames, string extraOptions,
|
||||
List<BinaryInclude.Excision> binaryIncludes) {
|
||||
PathNames = CommonUtil.Container.CopyStringList(pathNames);
|
||||
ExtraOptions = extraOptions;
|
||||
BinaryIncludes = binaryIncludes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -285,6 +285,24 @@ namespace SourceGen.AsmGen.WpfGui {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate binary includes.
|
||||
if (!BinaryInclude.PrepareList(res.BinaryIncludes, mWorkDirectory,
|
||||
out string failMsg)) {
|
||||
MessageBox.Show(this, "Failed processing binary includes: " + failMsg,
|
||||
Res.Strings.ERR_FILE_GENERIC_CAPTION,
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
} else {
|
||||
foreach (BinaryInclude.Excision exc in res.BinaryIncludes) {
|
||||
if (!BinaryInclude.GenerateOutputFile(exc, mProject.FileData,
|
||||
out string failMsg2)) {
|
||||
MessageBox.Show(this, "Failed processing binary include at +" +
|
||||
exc.Offset.ToString("x6") + ": " + failMsg2,
|
||||
Res.Strings.ERR_FILE_GENERIC_CAPTION,
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResetElements();
|
||||
mGenerationResults = res;
|
||||
previewFileComboBox.Items.Clear();
|
||||
|
@ -1189,6 +1189,9 @@ namespace SourceGen {
|
||||
if (type == DataType.Fill && subType != DataSubType.None) {
|
||||
throw new PluginException("SIDF rej: fill data must use subType=None");
|
||||
}
|
||||
if (type == DataType.BinaryInclude && subType != DataSubType.None) {
|
||||
throw new PluginException("SIDF rej: binary-include data must use subType=None");
|
||||
}
|
||||
|
||||
if (isStringType && !isStringSub) {
|
||||
throw new PluginException("SIDF rej: bad type/subType combo: type=" +
|
||||
@ -1275,6 +1278,8 @@ namespace SourceGen {
|
||||
return FormatDescriptor.Type.Uninit;
|
||||
case DataType.Dense:
|
||||
return FormatDescriptor.Type.Dense;
|
||||
case DataType.BinaryInclude:
|
||||
return FormatDescriptor.Type.BinaryInclude;
|
||||
default:
|
||||
Debug.Assert(false);
|
||||
throw new PluginException("Instr format rej: unknown format type " + pluginType);
|
||||
|
@ -152,7 +152,7 @@ namespace SourceGen {
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The contents of the 65xx data file.
|
||||
/// The contents of the 65xx data file. Do not modify.
|
||||
/// </summary>
|
||||
public byte[] FileData { get { return mFileData; } }
|
||||
private byte[] mFileData;
|
||||
|
@ -62,7 +62,8 @@ namespace SourceGen {
|
||||
Dense, // raw data, represented as compactly as possible
|
||||
Fill, // fill memory with a value
|
||||
Uninit, // uninitialized data storage area
|
||||
Junk // contents of memory are not interesting
|
||||
Junk, // contents of memory are not interesting
|
||||
BinaryInclude // file contents will be loaded from external file during asm
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -171,6 +172,19 @@ namespace SourceGen {
|
||||
/// </summary>
|
||||
public WeakSymbolRef SymbolRef { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional extra data, used for special cases like BinaryInclude. May be null.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It's unfortunate that we have this field for every object, even though very few
|
||||
/// will actually make use of it. The SymbolRef field has a very specific purpose
|
||||
/// and shouldn't be used to hold it (asserts and other logic gets upset). Storing
|
||||
/// the filenames in a separate table has some advantages, but requires integrating
|
||||
/// changes with the undo/redo mechanism, and the space savings doesn't justify the
|
||||
/// complexity cost.
|
||||
/// </remarks>
|
||||
public string Extra { get; private set; }
|
||||
|
||||
// Crude attempt to see how effective the prefab object creation is. Note we create
|
||||
// these for DefSymbols, so there will be one prefab for every platform symbol entry.
|
||||
public static int DebugCreateCount { get; private set; }
|
||||
@ -191,6 +205,7 @@ namespace SourceGen {
|
||||
Debug.Assert(length > 0);
|
||||
Debug.Assert(length <= MAX_NUMERIC_LEN || !IsNumeric);
|
||||
Debug.Assert(fmt != Type.Default || length == 1);
|
||||
Debug.Assert(fmt != Type.BinaryInclude);
|
||||
Debug.Assert(subFmt == SubType.None || (fmt != Type.Junk) ^ IsJunkSubType(subFmt));
|
||||
|
||||
Length = length;
|
||||
@ -213,6 +228,22 @@ namespace SourceGen {
|
||||
SymbolRef = sym;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for item with arbitrary string data.
|
||||
/// </summary>
|
||||
/// <param name="length">Length, in bytes.</param>
|
||||
/// <param name="fmt">Format type.</param>
|
||||
/// <param name="str">String data.</param>
|
||||
private FormatDescriptor(int length, Type fmt, string stringData) {
|
||||
Debug.Assert(length > 0);
|
||||
Debug.Assert(fmt == Type.BinaryInclude);
|
||||
Debug.Assert(!string.IsNullOrEmpty(stringData));
|
||||
Length = length;
|
||||
FormatType = fmt;
|
||||
FormatSubType = SubType.None;
|
||||
Extra = stringData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a descriptor with the requested characteristics. For common cases this
|
||||
/// returns a pre-allocated object, for less-common cases this allocates a new object.
|
||||
@ -267,6 +298,18 @@ namespace SourceGen {
|
||||
return new FormatDescriptor(length, sym, isBigEndian);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a descriptor with arbitrary string data.
|
||||
/// </summary>
|
||||
/// <param name="length">Length, in bytes.</param>
|
||||
/// <param name="fmt">Format type.</param>
|
||||
/// <param name="str">String data.</param>
|
||||
/// <returns>New or pre-allocated descriptor.</returns>
|
||||
public static FormatDescriptor Create(int length, Type fmt, string str) {
|
||||
DebugCreateCount++;
|
||||
return new FormatDescriptor(length, fmt, str);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the descriptor is okay to use on an instruction operand. The CPU only
|
||||
/// understands little-endian numeric values, so that's all we allow.
|
||||
@ -276,8 +319,8 @@ namespace SourceGen {
|
||||
switch (FormatType) {
|
||||
case Type.Default:
|
||||
case Type.NumericLE:
|
||||
//case Type.NumericBE:
|
||||
return true;
|
||||
//case Type.NumericBE:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -506,6 +549,9 @@ namespace SourceGen {
|
||||
case Type.Junk:
|
||||
retstr += "unaligned junk";
|
||||
break;
|
||||
case Type.BinaryInclude:
|
||||
retstr += "binary include";
|
||||
break;
|
||||
default:
|
||||
// strings handled earlier
|
||||
retstr += "???";
|
||||
@ -571,7 +617,7 @@ namespace SourceGen {
|
||||
|
||||
public override string ToString() {
|
||||
return "[FmtDesc: len=" + Length + " fmt=" + FormatType + " sub=" + FormatSubType +
|
||||
" sym=" + SymbolRef + "]";
|
||||
" sym=" + SymbolRef + " xtra=" + Extra + "]";
|
||||
}
|
||||
|
||||
|
||||
@ -583,7 +629,8 @@ namespace SourceGen {
|
||||
return false; // one is null
|
||||
}
|
||||
return a.Length == b.Length && a.FormatType == b.FormatType &&
|
||||
a.FormatSubType == b.FormatSubType && a.SymbolRef == b.SymbolRef;
|
||||
a.FormatSubType == b.FormatSubType && a.SymbolRef == b.SymbolRef &&
|
||||
a.Extra == b.Extra;
|
||||
}
|
||||
public static bool operator !=(FormatDescriptor a, FormatDescriptor b) {
|
||||
return !(a == b);
|
||||
@ -599,6 +646,9 @@ namespace SourceGen {
|
||||
hashCode ^= Length;
|
||||
hashCode ^= (int)FormatType;
|
||||
hashCode ^= (int)FormatSubType;
|
||||
if (Extra != null) {
|
||||
hashCode ^= Extra.GetHashCode();
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
|
@ -2352,6 +2352,7 @@ namespace SourceGen {
|
||||
Debug.WriteLine("No change to data formats");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void EditProjectProperties(WpfGui.EditProjectProperties.Tab initialTab) {
|
||||
|
@ -23,6 +23,10 @@ using System.Web.Script.Serialization;
|
||||
|
||||
using CommonUtil;
|
||||
|
||||
// TODO: experiment with serialization options that exclude default values, such as null
|
||||
// strings, from the serialized output
|
||||
// TODO: switch to System.Text.Json.JsonSerializer (with WriteIndented=true).
|
||||
|
||||
namespace SourceGen {
|
||||
/// <summary>
|
||||
/// Load and save project data from/to a ".dis65" file.
|
||||
@ -312,6 +316,7 @@ namespace SourceGen {
|
||||
public string Format { get; set; }
|
||||
public string SubFormat { get; set; }
|
||||
public SerWeakSymbolRef SymbolRef { get; set; }
|
||||
public string Extra { get; set; }
|
||||
|
||||
public SerFormatDescriptor() { }
|
||||
public SerFormatDescriptor(FormatDescriptor dfd) {
|
||||
@ -321,6 +326,9 @@ namespace SourceGen {
|
||||
if (dfd.SymbolRef != null) {
|
||||
SymbolRef = new SerWeakSymbolRef(dfd.SymbolRef);
|
||||
}
|
||||
if (dfd.Extra != null) {
|
||||
Extra = dfd.Extra;
|
||||
}
|
||||
}
|
||||
}
|
||||
public class SerWeakSymbolRef {
|
||||
@ -1087,9 +1095,14 @@ namespace SourceGen {
|
||||
": " + sfd.Format + "/" + sfd.SubFormat);
|
||||
return false;
|
||||
}
|
||||
if (sfd.SymbolRef == null) {
|
||||
if (sfd.Extra != null) {
|
||||
// Descriptor with extra data.
|
||||
dfd = FormatDescriptor.Create(sfd.Length, format, sfd.Extra);
|
||||
} else if (sfd.SymbolRef == null) {
|
||||
// Simple descriptor.
|
||||
dfd = FormatDescriptor.Create(sfd.Length, format, subFormat);
|
||||
} else {
|
||||
// Descriptor with symbolic reference.
|
||||
WeakSymbolRef.Part part;
|
||||
try {
|
||||
part = (WeakSymbolRef.Part)Enum.Parse(
|
||||
|
@ -81,6 +81,7 @@ namespace SourceGen {
|
||||
public string Uninit { get; private set; }
|
||||
public string Junk { get; private set; }
|
||||
public string Align { get; private set; }
|
||||
public string BinaryInclude { get; private set; }
|
||||
public string StrGeneric { get; private set; }
|
||||
public string StrReverse { get; private set; }
|
||||
public string StrLen8 { get; private set; }
|
||||
@ -133,6 +134,7 @@ namespace SourceGen {
|
||||
a.Uninit == b.Uninit &&
|
||||
a.Junk == b.Junk &&
|
||||
a.Align == b.Align &&
|
||||
a.BinaryInclude == b.BinaryInclude &&
|
||||
a.StrGeneric == b.StrGeneric &&
|
||||
a.StrReverse == b.StrReverse &&
|
||||
a.StrLen8 == b.StrLen8 &&
|
||||
@ -247,6 +249,7 @@ namespace SourceGen {
|
||||
{ "Uninit", ".ds" },
|
||||
{ "Junk", ".junk" },
|
||||
{ "Align", ".align" },
|
||||
{ "BinaryInclude", ".incbin" },
|
||||
|
||||
{ "StrGeneric", ".str" },
|
||||
{ "StrReverse", ".rstr" },
|
||||
@ -280,6 +283,7 @@ namespace SourceGen {
|
||||
case FormatDescriptor.Type.Fill:
|
||||
case FormatDescriptor.Type.Uninit:
|
||||
case FormatDescriptor.Type.Junk:
|
||||
case FormatDescriptor.Type.BinaryInclude:
|
||||
return 1;
|
||||
case FormatDescriptor.Type.Dense: {
|
||||
// no delimiter, two output bytes per input byte
|
||||
@ -389,6 +393,11 @@ namespace SourceGen {
|
||||
//po = outList[subIndex];
|
||||
}
|
||||
break;
|
||||
case FormatDescriptor.Type.BinaryInclude:
|
||||
po.Opcode = opNames.BinaryInclude;
|
||||
string biPath = AsmGen.BinaryInclude.ConvertPathNameFromStorage(dfd.Extra);
|
||||
po.Operand = '"' + biPath + "'";
|
||||
break;
|
||||
default:
|
||||
Debug.Assert(false);
|
||||
po.Opcode = ".???";
|
||||
|
BIN
SourceGen/SGTestData/20300-binary-include
Normal file
BIN
SourceGen/SGTestData/20300-binary-include
Normal file
Binary file not shown.
85
SourceGen/SGTestData/20300-binary-include.dis65
Normal file
85
SourceGen/SGTestData/20300-binary-include.dis65
Normal file
@ -0,0 +1,85 @@
|
||||
### 6502bench SourceGen dis65 v1.0 ###
|
||||
{
|
||||
"_ContentVersion":6,
|
||||
"FileDataLength":96,
|
||||
"FileDataCrc32":1316780246,
|
||||
"ProjectProps":{
|
||||
"CpuName":"6502",
|
||||
"IncludeUndocumentedInstr":false,
|
||||
"TwoByteBrk":false,
|
||||
"EntryFlags":32702671,
|
||||
"AutoLabelStyle":"Simple",
|
||||
"AnalysisParams":{
|
||||
"AnalyzeUncategorizedData":true,
|
||||
"DefaultTextScanMode":"LowHighAscii",
|
||||
"MinCharsForString":4,
|
||||
"SeekNearbyTargets":true,
|
||||
"UseRelocData":false,
|
||||
"SmartPlpHandling":false,
|
||||
"SmartPlbHandling":true},
|
||||
|
||||
"PlatformSymbolFileIdentifiers":[],
|
||||
"ExtensionScriptFileIdentifiers":[],
|
||||
"ProjectSyms":{
|
||||
}},
|
||||
|
||||
"AddressMap":[{
|
||||
"Offset":0,
|
||||
"Addr":4096,
|
||||
"Length":96,
|
||||
"PreLabel":"",
|
||||
"DisallowInward":false,
|
||||
"DisallowOutward":false,
|
||||
"IsRelative":false}],
|
||||
"TypeHints":[{
|
||||
"Low":0,
|
||||
"High":0,
|
||||
"Hint":"Code"}],
|
||||
"StatusFlagOverrides":{
|
||||
},
|
||||
|
||||
"Comments":{
|
||||
},
|
||||
|
||||
"LongComments":{
|
||||
},
|
||||
|
||||
"Notes":{
|
||||
},
|
||||
|
||||
"UserLabels":{
|
||||
"95":{
|
||||
"Label":"done",
|
||||
"Value":4191,
|
||||
"Source":"User",
|
||||
"Type":"GlobalAddr",
|
||||
"LabelAnno":"None"}},
|
||||
|
||||
"OperandFormats":{
|
||||
"23":{
|
||||
"Length":40,
|
||||
"Format":"BinaryInclude",
|
||||
"SubFormat":"None",
|
||||
"SymbolRef":null,
|
||||
"Extra":"↑20300-1.bin"},
|
||||
|
||||
"63":{
|
||||
"Length":32,
|
||||
"Format":"BinaryInclude",
|
||||
"SubFormat":"None",
|
||||
"SymbolRef":null,
|
||||
"Extra":"↑20300sub/20300-2.bin"}},
|
||||
|
||||
"LvTables":{
|
||||
},
|
||||
|
||||
"Visualizations":[],
|
||||
"VisualizationAnimations":[],
|
||||
"VisualizationSets":{
|
||||
},
|
||||
|
||||
"RelocList":{
|
||||
},
|
||||
|
||||
"DbrValues":{
|
||||
}}
|
17
SourceGen/SGTestData/Expected/20300-binary-include_64tass.S
Normal file
17
SourceGen/SGTestData/Expected/20300-binary-include_64tass.S
Normal file
@ -0,0 +1,17 @@
|
||||
.cpu "6502"
|
||||
* = $1000
|
||||
L1000 ldy #$28
|
||||
lda _L1017,y
|
||||
sta $2000,y
|
||||
dey
|
||||
bpl L1000
|
||||
lda _L103F
|
||||
lda _L103F+1
|
||||
lda done-1
|
||||
jmp done
|
||||
|
||||
_L1017 .binary "20300-1.bin"
|
||||
_L103F .binary "20300sub/20300-2.bin"
|
||||
|
||||
done rts
|
||||
|
17
SourceGen/SGTestData/Expected/20300-binary-include_acme.S
Normal file
17
SourceGen/SGTestData/Expected/20300-binary-include_acme.S
Normal file
@ -0,0 +1,17 @@
|
||||
!cpu 6502
|
||||
* = $1000
|
||||
L1000 ldy #$28
|
||||
lda @L1017,y
|
||||
sta $2000,y
|
||||
dey
|
||||
bpl L1000
|
||||
lda @L103F
|
||||
lda @L103F+1
|
||||
lda done-1
|
||||
jmp done
|
||||
|
||||
@L1017 !binary "20300-1.bin"
|
||||
@L103F !binary "20300sub/20300-2.bin"
|
||||
|
||||
done rts
|
||||
|
17
SourceGen/SGTestData/Expected/20300-binary-include_cc65.S
Normal file
17
SourceGen/SGTestData/Expected/20300-binary-include_cc65.S
Normal file
@ -0,0 +1,17 @@
|
||||
.setcpu "6502"
|
||||
.org $1000
|
||||
L1000: ldy #$28
|
||||
lda @L1017,y
|
||||
sta $2000,y
|
||||
dey
|
||||
bpl L1000
|
||||
lda @L103F
|
||||
lda @L103F+1
|
||||
lda done-1
|
||||
jmp done
|
||||
|
||||
@L1017: .incbin "20300-1.bin"
|
||||
@L103F: .incbin "20300sub/20300-2.bin"
|
||||
|
||||
done: rts
|
||||
|
@ -0,0 +1,9 @@
|
||||
# 6502bench SourceGen generated linker script for 20300-binary-include
|
||||
MEMORY {
|
||||
MAIN: file=%O, start=%S, size=65536;
|
||||
}
|
||||
SEGMENTS {
|
||||
CODE: load=MAIN, type=rw;
|
||||
}
|
||||
FEATURES {}
|
||||
SYMBOLS {}
|
@ -0,0 +1,17 @@
|
||||
org $1000
|
||||
L1000 ldy #$28
|
||||
lda :L1017,y
|
||||
sta $2000,y
|
||||
dey
|
||||
bpl L1000
|
||||
lda :L103F
|
||||
lda :L103F+1
|
||||
lda done-1
|
||||
jmp done
|
||||
|
||||
:L1017 hex 20212223242526272829303132333435363738394142434445464748494a5051
|
||||
hex 5253545556575859
|
||||
:L103F ds 32,$ff
|
||||
|
||||
done rts
|
||||
|
31
SourceGen/SGTestData/Source/20300-binary-include.S
Normal file
31
SourceGen/SGTestData/Source/20300-binary-include.S
Normal file
@ -0,0 +1,31 @@
|
||||
; Copyright 2024 faddenSoft. All Rights Reserved.
|
||||
; See the LICENSE.txt file for distribution terms (Apache 2.0).
|
||||
;
|
||||
; Assembler: Merlin 32
|
||||
;
|
||||
|
||||
org $1000
|
||||
start ldy #40
|
||||
lda blob1,Y
|
||||
sta $2000,Y
|
||||
dey
|
||||
bpl start
|
||||
|
||||
lda blob2
|
||||
lda blob2+1
|
||||
lda blob2+31
|
||||
|
||||
jmp done
|
||||
|
||||
; EDIT: set blob as binary include, current dir
|
||||
blob1
|
||||
asc ' !"#$%&',27,'()'
|
||||
asc '0123456789'
|
||||
asc 'ABCDEFGHIJ'
|
||||
asc 'PQRSTUVWXY'
|
||||
|
||||
; EDIT: set blob as binary include, file in subdir
|
||||
blob2
|
||||
ds 32,$ff
|
||||
|
||||
done rts
|
@ -74,6 +74,7 @@
|
||||
<Compile Include="AsmGen\AssemblerConfig.cs" />
|
||||
<Compile Include="AsmGen\AssemblerInfo.cs" />
|
||||
<Compile Include="AsmGen\AssemblerVersion.cs" />
|
||||
<Compile Include="AsmGen\BinaryInclude.cs" />
|
||||
<Compile Include="AsmGen\GenCommon.cs" />
|
||||
<Compile Include="AsmGen\IAssembler.cs" />
|
||||
<Compile Include="AsmGen\IGenerator.cs" />
|
||||
|
@ -309,6 +309,26 @@ namespace SourceGen.Tests {
|
||||
//continue;
|
||||
}
|
||||
|
||||
// Generate binary includes. These are not verified in the "expected source"
|
||||
// section because we'll do the necessary check in the binary diff.
|
||||
if (!BinaryInclude.PrepareList(genResults.BinaryIncludes, workDir,
|
||||
out string failMsg)) {
|
||||
ReportErrMsg("Failed processing binary includes: " + failMsg);
|
||||
ReportProgress("\r\n");
|
||||
didFail = true;
|
||||
} else {
|
||||
foreach (BinaryInclude.Excision exc in genResults.BinaryIncludes) {
|
||||
if (!BinaryInclude.GenerateOutputFile(exc, project.FileData,
|
||||
out string failMsg2)) {
|
||||
ReportErrMsg("Failed processing binary include at +" +
|
||||
exc.Offset.ToString("x6") + ": " + failMsg2);
|
||||
ReportProgress("\r\n");
|
||||
didFail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assemble code.
|
||||
ReportProgress(" " + asmId.ToString() + " assemble...");
|
||||
IAssembler asm = AssemblerInfo.GetAssembler(asmId);
|
||||
@ -604,8 +624,8 @@ namespace SourceGen.Tests {
|
||||
/// Removes the contents of a temporary work directory. Only files that we believe
|
||||
/// to be products of the generator or assembler are removed.
|
||||
/// </summary>
|
||||
/// <param name="workDir"></param>
|
||||
/// <param name="testNum"></param>
|
||||
/// <param name="workDir">Full pathname of work directory.</param>
|
||||
/// <param name="testNum">Test number, used to evaluate files for removal.</param>
|
||||
private void ScrubWorkDirectory(string workDir, int testNum) {
|
||||
string checkString = testNum.ToString();
|
||||
if (checkString.Length != 5) {
|
||||
@ -613,6 +633,20 @@ namespace SourceGen.Tests {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove any subdirectories that match the pattern, e.g. for binary includes.
|
||||
foreach (string pathName in Directory.EnumerateDirectories(workDir)) {
|
||||
string fileName = Path.GetFileName(pathName);
|
||||
if (fileName.Contains(checkString)) {
|
||||
ScrubWorkDirectory(pathName, testNum);
|
||||
try {
|
||||
Directory.Delete(pathName);
|
||||
} catch (Exception ex) {
|
||||
ReportErrMsg("unable to remove dir '" + fileName + "': " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all matching files.
|
||||
foreach (string pathName in Directory.EnumerateFiles(workDir)) {
|
||||
bool doRemove = false;
|
||||
string fileName = Path.GetFileName(pathName);
|
||||
|
@ -79,13 +79,13 @@ namespace SourceGen {
|
||||
public bool IsVariable { get { return VarType != LocalVariableType.NotVar; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// Standard constructor.
|
||||
/// </summary>
|
||||
public WeakSymbolRef(string label, Part part) :
|
||||
this(label, part, LocalVariableType.NotVar) { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// Constructor for local variable table references.
|
||||
/// </summary>
|
||||
public WeakSymbolRef(string label, Part part, LocalVariableType varType) {
|
||||
Debug.Assert(label != null);
|
||||
|
@ -653,6 +653,12 @@ limitations under the License.
|
||||
VerticalAlignment="Center" Margin="{StaticResource TBS}"
|
||||
Text=".placeho" MaxLength="12"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"/>
|
||||
<TextBlock Grid.Column="4" Grid.Row="3" Text="Binary include:"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
||||
<TextBox Name="binaryIncludeTextBox" Grid.Column="5" Grid.Row="3"
|
||||
VerticalAlignment="Center" Margin="{StaticResource TBS}"
|
||||
Text=".placeho" MaxLength="12"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"/>
|
||||
<TextBlock Grid.Column="6" Grid.Row="3" Text="Bulk data:"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
||||
<TextBox Name="denseTextBox" Grid.Column="7" Grid.Row="3"
|
||||
|
@ -1365,6 +1365,7 @@ namespace SourceGen.WpfGui {
|
||||
new TextBoxPropertyMap(uninitTextBox, "Uninit"),
|
||||
new TextBoxPropertyMap(junkTextBox, "Junk"),
|
||||
new TextBoxPropertyMap(alignTextBox, "Align"),
|
||||
new TextBoxPropertyMap(binaryIncludeTextBox, "BinaryInclude"),
|
||||
new TextBoxPropertyMap(strGenericTextBox, "StrGeneric"),
|
||||
new TextBoxPropertyMap(strReverseTextBox, "StrReverse"),
|
||||
new TextBoxPropertyMap(strLen8TextBox, "StrLen8"),
|
||||
|
@ -148,9 +148,17 @@ limitations under the License.
|
||||
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
|
||||
<RadioButton Name="radioJunk" GroupName="Main" Content="_Junk bytes, end aligned to"
|
||||
Checked="MainGroup_CheckedChanged"/>
|
||||
<ComboBox Name="junkAlignComboBox" Margin="8,-5,0,0" Width="200"
|
||||
<ComboBox Name="junkAlignComboBox" Margin="6,-4,0,0" Width="200"
|
||||
ItemsSource="{Binding JunkAlignmentItems}" DisplayMemberPath="Description"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
|
||||
<RadioButton Name="radioBinaryInclude" GroupName="Main" Content="Binary include, filename:"
|
||||
Checked="MainGroup_CheckedChanged"/>
|
||||
<TextBox Name="binaryIncludeTextBox" Margin="6,-2,0,0" Width="240"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"
|
||||
TextChanged="BinaryIncludeTextBox_TextChanged"/>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Text="String" Margin="0,12,0,0"/>
|
||||
<Rectangle HorizontalAlignment="Stretch" Fill="LightGray" Height="2"/>
|
||||
|
@ -266,6 +266,12 @@ namespace SourceGen.WpfGui {
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
private void BinaryIncludeTextBox_TextChanged(object sender, TextChangedEventArgs e) {
|
||||
radioBinaryInclude.IsChecked = true;
|
||||
// Update OK button based on filename validity.
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the string encoding combo box to an item that matches the specified mode. If
|
||||
/// the mode can't be found, an arbitrary entry will be chosen.
|
||||
@ -377,6 +383,10 @@ namespace SourceGen.WpfGui {
|
||||
}
|
||||
IsValid = isOk;
|
||||
|
||||
if (radioBinaryInclude.IsChecked == true) {
|
||||
IsValid &= AsmGen.BinaryInclude.ValidatePathName(binaryIncludeTextBox.Text);
|
||||
}
|
||||
|
||||
// If dense hex with a limit is selected, check the value.
|
||||
if (radioDenseHexLimited.IsChecked == true) {
|
||||
if (MaxDenseBytesPerLine > 0) {
|
||||
@ -514,6 +524,9 @@ namespace SourceGen.WpfGui {
|
||||
radioFill.IsEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// We can't handle multiple ranges because we need to set the filename.
|
||||
radioBinaryInclude.IsEnabled = (mSelection.RangeCount == 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -901,6 +914,11 @@ namespace SourceGen.WpfGui {
|
||||
case FormatDescriptor.Type.Junk:
|
||||
preferredFormat = radioJunk;
|
||||
break;
|
||||
case FormatDescriptor.Type.BinaryInclude:
|
||||
preferredFormat = radioBinaryInclude;
|
||||
binaryIncludeTextBox.Text =
|
||||
AsmGen.BinaryInclude.ConvertPathNameFromStorage(dfd.Extra);
|
||||
break;
|
||||
default:
|
||||
// Should not be here.
|
||||
Debug.Assert(false);
|
||||
@ -1077,6 +1095,10 @@ namespace SourceGen.WpfGui {
|
||||
type = FormatDescriptor.Type.Junk;
|
||||
JunkAlignmentItem comboItem = (JunkAlignmentItem)junkAlignComboBox.SelectedItem;
|
||||
subType = comboItem.FormatSubType;
|
||||
} else if (radioBinaryInclude.IsChecked == true) {
|
||||
type = FormatDescriptor.Type.BinaryInclude;
|
||||
// path will be extracted directly by subroutine
|
||||
Debug.Assert(mSelection.RangeCount == 1);
|
||||
} else if (radioStringMixed.IsChecked == true) {
|
||||
type = FormatDescriptor.Type.StringGeneric;
|
||||
subType = charSubType;
|
||||
@ -1176,7 +1198,13 @@ namespace SourceGen.WpfGui {
|
||||
// The one exception to this is ASCII values for non-string data, because we have
|
||||
// to dig the low vs. high value out of the data itself.
|
||||
FormatDescriptor dfd;
|
||||
if (subType == FormatDescriptor.SubType.Symbol) {
|
||||
if (type == FormatDescriptor.Type.BinaryInclude) {
|
||||
// Special case. We know there can be only one of these, so just grab the
|
||||
// filename directly instead of passing it in as a rare argument.
|
||||
string storePath =
|
||||
AsmGen.BinaryInclude.ConvertPathNameToStorage(binaryIncludeTextBox.Text);
|
||||
dfd = FormatDescriptor.Create(chunkLength, type, storePath);
|
||||
} else if (subType == FormatDescriptor.SubType.Symbol) {
|
||||
dfd = FormatDescriptor.Create(chunkLength, symbolRef,
|
||||
type == FormatDescriptor.Type.NumericBE);
|
||||
} else {
|
||||
|
@ -695,6 +695,7 @@ namespace SourceGen.WpfGui {
|
||||
case FormatDescriptor.Type.Fill:
|
||||
case FormatDescriptor.Type.Uninit:
|
||||
case FormatDescriptor.Type.Junk:
|
||||
case FormatDescriptor.Type.BinaryInclude:
|
||||
default:
|
||||
// Unexpected; used to be data?
|
||||
break;
|
||||
|
@ -273,6 +273,17 @@ you can mark it as <samp>Junk</samp>. If it appears to be adding bytes
|
||||
to reach a power-of-two address boundary, you can designate it as an alignment
|
||||
directive. If you have multiple regions selected, only the alignment
|
||||
options that work for all regions will be shown.</p>
|
||||
<p>If you want to import a section of the file as a binary file, rather
|
||||
than representing it in the assembly source, you can set the region as
|
||||
a <samp>Binary Include</samp>. These sections must be for a single
|
||||
unbroken section of the file. Assign a filename to use for the output
|
||||
file. Filenames may be partial paths, but may not reference directories
|
||||
above the project directory (with "..") or include double quotes (which
|
||||
would require escaping in the assembler output). Each binary include
|
||||
directive must output to a different filename (case-insensitive). During
|
||||
assembly source generation, existing files will only be overwritten if
|
||||
they have the same length as the binary include; if they have a different
|
||||
length, an error will be reported.</p>
|
||||
|
||||
<p>The <samp>String</samp> items are enabled or disabled depending on
|
||||
whether the data you have selected is in the appropriate format. For example,
|
||||
|
Loading…
Reference in New Issue
Block a user